5

Why does the following print 2?

list<int> l;
l.push_back( 1 );
l.push_back( 2 );
l.push_back( 3 );
list<int>::iterator i = l.begin();
i++;
l.erase( i );
cout << *i;

I know what erase returns, but I wonder why this is OK? Or is it undefined, or does it depend on the compiler?

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
Kiril Kirov
  • 37,467
  • 22
  • 115
  • 187

5 Answers5

15

Yes, it's undefined behaviour. You are dereferencing a kind of wild pointer. You shouldn't use the value of i after erase.

And yes, erase destructs the object pointed to. However, for POD types destruction doesn't do anything.

erase doesn't assign any special "null" value to the iterator being erased, the iterator is just not valid any more.

Vlad
  • 35,022
  • 6
  • 77
  • 199
  • Does erase *really* destroy the object it's pointing to? Like for instance of a list of pointers, does erase destroy the pointer? My understanding is that it doesn't. – Vite Falcon Mar 11 '11 at 14:30
  • @Vite: what is "really" in this context? The `erase` calls the destructor on the iterator target. It doesn't null out the memory location where the iterator target is located. Beware that if your list contains pointers, `delete`ing a pointer is not the same as `delete`ing the object the pointer points to! Destroying a pointer is effectively a no-op. – Vlad Mar 11 '11 at 14:33
  • 1
    @Vite Falcon It destroys the *pointer*...NOT what the pointer points TO. – Mark B Mar 11 '11 at 14:34
  • @Vite - depends on what you understand from "destroy". Destructor is called for sure, always (on erase). But it seems the memory is just marked as "free", for simple objects/vars. – Kiril Kirov Mar 11 '11 at 14:34
  • @Kiril: yes, usually the memory is just marked free, like always after `delete`. – Vlad Mar 11 '11 at 14:38
  • @Vlad - sure, it seems to be like HDD.. 10x – Kiril Kirov Mar 11 '11 at 14:42
  • @Kiril: I guess wiping the memory out is considered to be just a waste of time - indeed, the same way as for the filesystems. – Vlad Mar 11 '11 at 14:44
2

"destroying" an object means its memory is reclaimed and its content might be altered (mainly if the hand-written destructor do so, and possibly as a result of storing free-memory-related stuff in place). list::erase returns you a new iterator that you should use instead of the one that was passed as argument (I'd be tempted to make i = l.erase(i); an habbit).

In no way do destruction imply that memory is swept, wiped out. Previously valid location are still valid in most cases from the CPU's point of view (ie. it can fetch values), but can't be relied on because other operation may recycle that location for any purpose at any time.

You're unlikely to see *i throwing a segfault, imho -- although that might happen with more complex types that use pointers, but you might see it having new values.

Other collections might have a more previsible behaviour than list. IIrc, a vector would compact the storage area so the previous value would only be seen by further dereferencing i in rare cases.

PypeBros
  • 2,607
  • 24
  • 37
  • Yep, I know how to use it (`i = l.erase(i);`), I was just wondering (: – Kiril Kirov Mar 11 '11 at 14:30
  • @kiril kirov: I figured that out from your question, but thought the answer would be more complete with that information as well. Hope the rest helps demystifying the behaviour. – PypeBros Mar 11 '11 at 14:34
1

Seems like iterator still points to this memory....
If you would write something to this block,
maybe next time *i would throw segmentation fault..

sorry for speculation though

bua
  • 4,761
  • 1
  • 26
  • 32
1

Since you are dealing with a linked list, the elements of the list need not to be right "behind" each other in memory. If you would try the same thing with a vector, you would likely (since the behavior is undefined) experience

cout << *i

to be printed as 2.

However, this is not a very safe way of programming. So once you erase an iterator make sure not to use it again, unless you initialize it again with begin() or end() etc.

Woltan
  • 13,723
  • 15
  • 78
  • 104
1

Try compiling your code with the correct options, with a good compiler, then running it. (With VC++, /D_DEBUG /EHs /MDd seems to be sufficient. With g++, -D_GLIBCXX_CONCEPT_CHECKS -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC, at least. Both compilers need even more options in general.) It should crash. (It does when I tried it.)

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
James Kanze
  • 150,581
  • 18
  • 184
  • 329