我正在试图找出std::multimap
迭代器是如何工作的,因此我创建了一个简单的例子来说明问题的实质。如果取消注释情况1,我希望迭代器指向带有键1的第一个元素,但实际上它会打印与键0相关的所有值(就像没有被擦除一样)并且有时会崩溃,可能是因为迭代器无效。但是,如果取消注释案例2,则会正确删除键1的所有值。
有没有办法知道擦除后multimap
的下一个有效迭代器是什么?
(例如std::vector.erase(...)
返回一个)
std::multimap<int, int> m;
for(int j=0; j<3; ++j) {
for(int i=0; i<5; ++i) {
m.insert(std::make_pair(j, i));
}
}
for(std::multimap<int, int>::iterator it=m.begin(); it!=m.end();) {
printf("%d %d\n", (*it).first, (*it).second);
++it;
if( (*it).second == 3 ) {
//m.erase(0); //case 1
m.erase(1); //case 2
}
}
答案 0 :(得分:3)
当您在示例中致电m.erase(0)
时,it
指向包含密钥0
的元素 - 因此it
无效。 m.erase(1)
有效,因为第一次调用it
时,1
未指向具有键1
的元素,因此不受影响。在以后的迭代中,没有任何带有键multimap
的元素,因此不会删除任何内容,也不会影响迭代器。
erase
没有返回下一个有效迭代器的it = m.upper_bound(deleted_key);
方法。一种替代方法是在删除后调用erase(x)
。这是对数的,对于您的场景可能太慢(upper_bound
和erase
将是两个对数操作。)
假设您要删除迭代器当前指向的键,您可以执行以下操作(否则,std::multimap<int, int>::iterator interval_start = m.begin();
for(std::multimap<int, int>::iterator it=m.begin(); it!=m.end(); ++it) {
if(interval_start->first < it->first) // new interval starts here
interval_start == it;
if( (*it).second == 3 ) {
std::multimap<int, int>::iterator interval_end = it;
while((interval_end != m.end()) && (interval_end->first == it->first)) {
++interval_end; // search for end of interval - O(n)
}
m.erase(interval_start, interval_end); // erase interval - amortized O(1)
it = interval_end; // set it to first iterator that was not erased
interval_start = interval_end; // remember start of new interval
}
}
当然没有问题;未经测试):
upper_bound
这使用一个线性操作,其余都是恒定时间。如果您的地图非常大,并且您只有几个具有相同键的项目,那么这可能会更快。但是,如果您有许多具有相同键的项目,则在搜索间隔结束时使用O(log n)
(O(n)
而不是{{1}}可能更有效地搜索间隔结束)。
答案 1 :(得分:2)
擦除迭代器时变为无效。而是记住下一个元素然后擦除:
std::map<int,int>::iterator next = m + 1;
m.erase
m = next;
答案 2 :(得分:1)
std::multimap<int, int> m;
// ^^^^^^^^
std::map<int, int>::iterator it=m.begin();
// ^^^
...坎
for(std::multimap<int, int>::iterator it=m.begin(); it!=m.end();) {
.... stuff ....
m.erase(1); // container mutation
.... stuff ....
}
当你在迭代容器(任何容器)时,要非常小心,因为你可能会使你依赖的迭代器失效。
所谓的“基于节点的容器”(list
,set
,map
...)是最健壮的容器WRT迭代器失效:它们只会使删除的迭代器无效元素(没有办法使这些迭代器无效)。
在这种情况下,您应该检查您要删除的元素实际上不是*it
。
我不太确定你在尝试用你的循环做什么。
答案 3 :(得分:0)
从查看代码开始,我认为你的++导致问题。您将其分配给可能已被删除的地方。在if语句和测试之后将其移动到最后。像这样:
for(std::multimap<int, int>::iterator it=m.begin(); it!=m.end();) {
printf("%d %d\n", (*it).first, (*it).second);
if( (*it).second == 3 ) {
//m.erase(0); //case 1
m.erase(1); //case 2
}
++it;
}
答案 4 :(得分:0)
(适用编者)强>
for(std::multimap<int, int>::iterator it=m.begin(); it!=m.end();) {
printf("%d %d\n", (*it).first, (*it).second);
++it;
if( (*it).second == 3 ) {
//m.erase(0); //case 1
m.erase(1); //case 2
}
}
除了根据it
的内容m.erase
导致的multimap
迭代器失效之外(已在另一个答案中已经介绍过),总是存在您取消引用的问题{ {1}}每次运行程序m.end()
时for
循环的最后一次迭代的迭代器。
我建议使用调试版本运行和调试。我几乎可以肯定每个理智的标准库实现都应该包含断言来检测if( (*it).second == 3 )
解除引用。
答案 5 :(得分:0)
上面的一些人已经回答说你正在玩火。
另外,我认为您忘记了multimap是有序映射,因此您将从最小的键迭代到最大的键。因此,在第一种情况下,您在打印其中一些后删除了键,但在第二种情况下,您在去往它们之前就将其删除。