2012-11-19 39 views
2

我有这个示例代码将条目插入到multimap中。我试图删除指定键的特定条目。但是这段代码进入了无限循环。有人可以帮我解决这个问题吗?在C++中删除特定键的条目STL multimap

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

回答

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,因此它会一直持续下去。然而,它仍然是你应该修复的事情,因为它可能导致不可预知的并且很难追踪错误。

+0

@dbrown,我该如何解决这个问题?我试着将迭代器复制到一个临时的,并使用该临时迭代器擦除,但它不起作用 – Santhosh

+0

@ skokal01更新了一个示例修复 –

2

正如其他人所说,推进一个指向刚刚被擦除的元素的迭代器并不能保证能够正常工作。你可以做的反而是使用后缀++操作来获取一个迭代随后的擦写元素也被删除前:

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将使这更好看。