R@M3$H.NBlog

Delete An Entry in STL containers

02 May, 2013 - 2 min read

For Sequence Containers:

for(MyMap::iterator it = mymap.begin(); it!=mymap.end(); ) {
  if(mycondition(it))
    it = mymap.erase(it);
  else
    it++;
}

This will work for sequence containers like std::vector, but not for associative containers like std::map. The difference is that in sequence containers erase() returns an iterator to the next element, whereas in associative containers it returns void.

For Associative Containers:

one approach:

std::map<K, V>::iterator itr = myMap.begin();
while (itr != myMap.end()) {
    if (ShouldDelete(*itr)) {
       std::map<K, V>::iterator toErase = itr;
       ++itr;
       myMap.erase(toErase);
    } else {
       ++itr;
    }
}

The idea is to walk the iterator forward from the start of the container to the end, checking at each step whether the current key/value pair should be deleted. If so, a copy of the iterator is made and the iterator is advanced to the next step (to avoid iterator invalidation), then the copied iterator is removed from the container. Otherwise, the iterator is advanced as usual.

Another common approach is seen here:

std::map<K, V>::iterator itr = myMap.begin();
while (itr != myMap.end()) {
    if (ShouldDelete(*itr)) {
       myMap.erase(itr++); //Advance before Itr becomes Invalid
    } else {
       ++itr;
    }
}

This uses the fact that itr++ returns the value that the old iterator used to have as a side-effect of advancing it forward a step.

A variation of Mark Ransom algorithm but without the need for a temporary.

for(Actions::iterator it = _actions.begin();it != _actions.end();)
{
    if (expired(*it))
    {
        bar(*it);
        _actions.erase(it++);  // Note the post increment here.
                               // This increments 'it' and returns a copy of
                               // the original 'it' to be used by erase()
    }
    else
    {
        ++it;  // Use Pre-Increment here as it is more effecient
               // Because no copy of it is required.
    }
}

You can post-increment the iterator while passing it as argument to erase:

myMap.erase(itr++)

This way, the element that was pointed by itr before the erase is deleted, and the iterator is incremented to point to the next element in the map. If you're doing this in a loop, beware not to increment the iterator twice.

Best way to delete an entry map<int, A*>

Delete object first, then remove from map. Otherwise you're just introducing a pointless intermediate variable for storing the pointer. As long as you're singlethreaded, or have proper locking in a multithreaded scenario, the two methods are for all practical purposes equivalent.

map&lt;int, A *&gt;::iterator it = mymap.find(1);
if (it != mymap.end()) {
  delete it-&gt;second;
  mymap.erase(it);
} 

End