删除C ++ STL multimap中特定键的条目

时间:2012-11-19 00:32:19

标签: c++ stl

我有这个示例代码将条目插入到多图。我试图删除指定键的特定条目。但是这段代码进入无限循环。有人可以帮我这个代码吗?

#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;
}

在上面我正在查找使用键值“Z”并想要删除“A”。

2 个答案:

答案 0 :(得分:5)

multimap::erase使擦除元素的任何迭代器无效,因此行

names.erase(p);
p++;

擦除p,从而使其无效,然后尝试递增无效的迭代器。您可以通过将p复制到临时递增p,然后擦除临时迭代器来解决此问题。

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

或者,如果您使用的是C ++ 11,那么multimap::erase将返回容器中的下一个迭代器

p = names.erase(p);

编辑:上面实际上并不是你的无限循环的来源。在第二个循环中,您不会增加p,因此它将永远消失。然而,它仍然是你应该修复的东西,因为它可能导致不可预测的并且难以追踪错误。

答案 1 :(得分:2)

正如其他人所说的那样,推进指向刚被删除的元素的迭代器并不能保证能够正常工作。你可以做的是使用postfix ++运算符来检索一个迭代器,该迭代器是在擦除之前删除的之后的元素

names.erase(p++);

在C ++ 11中,您也可以检索erase的返回值,该值指向以下元素(如果没有更多元素,则为end()):

p = names.erase(p);

根据定义,你的第二个循环也是无限循环,因为它永远不会增加计数器。

但是,还有一件事要说:检查一系列元素中的最后一个元素是否已达到的方法效率不高:在循环的每次迭代中调用upper_bound,这将导致每次都进行新的O(log(n))树搜索,尽管返回的迭代器将始终相同。

通过在进入循环并存储结果之前运行upper_bound,您可以明显改善这一点。但更好的是,我建议你运行一次equal_range函数,然后简单地遍历它返回的范围:

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;

在C ++ 11中,使用auto会使这看起来更好。