简体   繁体   中英

Removing entries of particular key in C++ STL multimap

I have this sample code to insert entries to a multimap. I am trying to delete particular entries of a specified key. But this code goes into infinite loop. Can someone help me with this code?

#include <iostream>
#include <map>
#include <string>
using namespace std;

int main()
{
    multimap<string, string> names;
    string n;

    names.insert(pair<string, string>("Z", "F"));
    names.insert(pair<string, string>("Z", "A"));

    names.insert(pair<string, string>("S", "T"));
    names.insert(pair<string, string>("S", "A"));
    names.insert(pair<string, string>("S", "J"));

    names.insert(pair<string, string>("D", "H"));
    names.insert(pair<string, string>("D", "W"));
    names.insert(pair<string, string>("D", "R"));

    multimap<string, string>::iterator p;


    p = names.find("Z");
    if(p != names.end()) { // found a name
        do {
            cout << n << ", " << p->second;
            cout << endl;
            if (p->second.compare("A") == 0) {
                names.erase(p);
                p++;
            } else {
                p++;
            }
        } while (p != names.upper_bound("Z"));
    }
    else{
        cout << "Name not found.\n";
    }

    p = names.find("Z");
    if(p != names.end()) { // found a name
        do {
            cout << n << ", " << p->second;
            cout << endl;
        } while (p != names.upper_bound("Z"));
    }
    else{
        cout << "Name not found.\n";
    }
    return 0;
}

In the above I am looking up using Key value "Z" and want to delete "A".

multimap::erase invalidates any iterators to the erase elements, so the lines

names.erase(p);
p++;

erases p, thus invalidating it, and then attempt to increment an invalid iterator. You can fix this by copying p to a temporary, incrementing p, and then erasing the temporary iterator.

multimap<string, string>::iterator temp = p;
++p;
names.erase(temp);

Alternatively if you're using C++11 then multimap::erase returns the next iterator in the container

p = names.erase(p);

Edit: the above isn't actually the source of your infinite loop. In the second loop you don't increment p , so it goes forever. However it is still something you should fix as it can cause unpredictable and difficult to track down bugs.

As said by others, advancing an iterator that points to an element that was just erased is not guaranteed to work. What you can do instead is to use the postfix ++ operator to retrieve an iterator to the element that followed the erased one before it was erased :

names.erase(p++);

In C++11, you can alternatively retrieve the return value of erase , which points to the following element (or is end() if there is no more element):

p = names.erase(p);

It has also been said already that your second loop is an infinite loop by definition because it never increments the counter.

However, there is one more thing that should be said: Your method of checking if the last element in a range of elements has been reached is not very efficient: You call upper_bound in every iteration of the loop, which will cause a new O(log(n)) tree search each time, although the iterator returned will always be the same.

You can obviously improve this by running upper_bound before you enter the loop and store the result. But even better, I'd suggest your run the equal_range function once, and then simply iterate through the range it returned:

typedef multimap<string,string>::const_iterator mapit;
std::pair<mapit,mapit> range = names.equal_range("Z");
mapit it = range.first;
while (it != range.second)
  if (it->second == "A")
    names.erase(it++);
  else
    ++it;

In C++11, the use of auto will make this look even better.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM