C++中map的迭代器失效问题

3

我有一个示例程序,想要查看在从地图中删除元素时,迭代器如何失效。

程序如下:

#include <iostream>
#include <map>

using namespace std;

int main(int argc, char *argv[])
{

    map<int, int> myMap;

    myMap.insert(pair<int, int>(0, 2));    
    myMap.insert(pair<int, int>(1, 4));    
    myMap.insert(pair<int, int>(3, 18));    
    myMap.insert(pair<int, int>(2, 20));    

    map<int, int>::iterator it; 

    for(it = myMap.begin(); it != myMap.end(); ++it) 
    {   
        myMap.erase(it);  // erasing the element pointed at by iterator

        cout << it->first << endl; // iterator is invalid here 
    }   
    return 0;
}

问题是我得到的输出是:
0
1
2
3  

为什么迭代器没有失效并且给出了错误的结果。非常感谢您提供任何帮助。

C++ STL maps的文档说明:对已经删除的元素的引用和迭代器将会失效,而其他引用和迭代器则不受影响。


4
你在问为什么使用一个失效的迭代器有时候会起作用?这有点像问为什么有些人跳伞不带降落伞却能幸存。 - UKMonkey
您的迭代器已失效。您正在观察未定义行为的影响。 - Ron
解除无效的迭代器是未定义行为。它可以执行任何操作,包括返回(表面上)正确的值。 - Yksisarvinen
那么,你的意思是它确实是无效的吗? - Bhawan
是的,您正在调用未定义的行为。 - Ron
3个回答

4
使用无效的迭代器是未定义行为。在这种情况下,任何事情都可能发生。
为什么你会看到这些值?迭代器包含一个指向某个内存块的指针,仅仅因为偶然的原因,这个内存块尚未被返回给系统并且尚未被覆盖。这就是为什么你仍然可以看到已经“死亡”的值。
这并不改变任何东西,它仍然是未定义的行为,下一次运行程序时,映射元素所在的内存页可能已经被返回给操作系统,你会收到访问冲突(分段错误)...

2

默认情况下,没有针对无效迭代器的运行时检查。

您可以通过为GNU C++标准库启用-D_GLIBCXX_DEBUG来启用无效迭代器的调试检查。这将产生以下运行时错误:

iterator "this" @ 0x0x7fff9f3d7060 {
type = N11__gnu_debug14_Safe_iteratorISt17_Rb_tree_iteratorISt4pairIKiiEENSt7__debug3mapIiiSt4lessIiESaIS4_EEEEE (mutable iterator);
  state = singular;
  references sequence with type `NSt7__debug3mapIiiSt4lessIiESaISt4pairIKiiEEEE' @ 0x0x7fff9f3d7150
}

如需查看其他标准库,请查阅文档。


请注意,这是在 UB 的范围内进行的操作:标准规定“任何事情都可能发生”,然后实现会说“抛出异常符合任何情况”。 - Caleth
按照这种逻辑的极端情况,即使没有调试迭代器,它也可以打印此消息,但这种情况非常罕见,尚未观察到。严谨地说,标准在UB发生时不需要任何特定行为,这与“任何事情都可能发生”相当不同。 - Maxim Egorushkin
1
“不需要”任何特定行为,但也“不禁止”任何特定行为。换句话说,“任何事情都可能发生”。 - Caleth

2

失效的迭代器并不意味着其内部数据被删除。有时候,就像在这种情况下一样,失效的迭代器可能仍然持有对下一个项目的有效引用。但是,这样使用它是未定义行为,很可能会在您的应用程序中引起一些问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接