C ++ multimap迭代器失效

时间:2011-12-23 07:23:57

标签: c++ stl iterator multimap

我正在试图找出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
    }
}

6 个答案:

答案 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_bounderase将是两个对数操作。)

假设您要删除迭代器当前指向的键,您可以执行以下操作(否则,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 ....
}

当你在迭代容器(任何容器)时,要非常小心,因为你可能会使你依赖的迭代器失效。

所谓的“基于节点的容器”(listsetmap ...)是最健壮的容器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是有序映射,因此您将从最小的键迭代到最大的键。因此,在第一种情况下,您在打印其中一些后删除了键,但在第二种情况下,您在去往它们之前就将其删除。