Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problem with std::map::iterator after calling erase()

Tags:

c++

iterator

// erasing from map
#include <iostream>
#include <map>
using namespace std;

int main ()
{
  map<char,int> mymap;
  map<char,int>::iterator it(mymap.begin());

  // insert some values:
  mymap['a']=10;
  mymap['b']=20;
  mymap['c']=30;
  mymap['d']=40;
  mymap['e']=50;
  mymap['f']=60;

  it=mymap.find('a');
  mymap.erase (it);                   // erasing by iterator

  // show content:
  for (; it != mymap.end(); it++ )
    cout << (*it).first << " => " << (*it).second << endl;
  return 0;
}

Why does this give an output like

a => 10
b => 20
c => 30
d => 40
e => 50
f => 60

shouldn't "a => 10" be deleted anyways, but if I declare it = mymap.begin() in the for loop, everything is perfect. why?

program adapted from : http://www.cplusplus.com/reference/stl/map/erase/

like image 960
0x0 Avatar asked Jan 08 '11 21:01

0x0


People also ask

What happens to iterator after erase?

Every iterator and reference after the point of erasing is invalidated. Only the iterators and references to the erased element is invalidated. Only iterators and references to the erased elements are invalidated.

How do you clear a std::map in C++?

map::clear() function is an inbuilt function in C++ STL, which is defined in header file. clear() is used to remove all the content from the associated map container. This function removes all the values and makes the size of the container as 0.

Does map insert invalidate iterator?

Inserting into std::map does not invalidate existing iterators.

Does map return an iterator?

map() loops over the items of an input iterable (or iterables) and returns an iterator that results from applying a transformation function to every item in the original input iterable.


2 Answers

Erasing an element of a map invalidates iterators pointing to that element (after all that element has been deleted). You shouldn't reuse that iterator.

Since C++11 erase() returns a new iterator pointing to the next element, which can be used to continue iterating:

it = mymap.begin();
while (it != mymap.end()) {
   if (something)
      it = mymap.erase(it);
   else
      it++;
}

Before C++11 you would have to manually advance the iterator to the next element before the deletion takes place, for example like this:

mymap.erase(it++);

This works because the post-increment side-effect of it++ happens before erase() deletes the element. Since this is maybe not immediately obvious, the C++11 variant above should be preferred.

like image 88
sth Avatar answered Oct 12 '22 12:10

sth


Calling erase() invalidates the iterator. In this case, what's happening is the iterator points to the residual value left behind in memory (but don't rely on this undefined behaviour!). Reset the iterator with it=mymap.begin() before the loop for the desired results.

http://codepad.org/zVFRtoV5

This answer shows how to erase elements while iterating over an std::map:

for(map<T, S*>::iterator it = T2pS.begin(); it != T2pS.end(); T2pS.erase(it++)) {
    // wilhelmtell in the comments is right: no need to check for NULL. 
    // delete of a NULL pointer is a no-op.
    if(it->second != NULL) {
        delete it->second;
            it->second = NULL;
    }
}
like image 40
moinudin Avatar answered Oct 12 '22 10:10

moinudin