2

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

#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”。

4

2 回答 2

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,所以它会永远存在。但是,它仍然是您应该修复的问题,因为它可能会导致不可预测且难以追踪错误。

于 2012-11-19T00:39:32.973 回答
2

正如其他人所说,推进指向刚刚被擦除的元素的迭代器并不能保证工作。相反,您可以做的是使用后缀运算符检索一个迭代器,该迭代器指向在擦除之前++跟随已擦除元素的元素:

names.erase(p++);

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

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会使这看起来更好。

于 2012-11-19T01:04:29.447 回答