为何erase后用原迭代器遍历std::set会出现异常输出?
问题根源:迭代器失效导致的未定义行为
嘿,这个问题其实是std::set迭代器失效引发的典型场景,我来给你掰扯清楚~
首先你得搞懂std::set的迭代器规则:当你调用erase(11)删除元素时,之前通过find(11)拿到的迭代器it会直接失效。因为这个迭代器指向的节点已经被从红黑树(set的底层实现结构)里移除销毁了,此时它就变成了「野迭代器」——指向的是已经被释放的内存空间。
为什么用失效迭代器遍历会输出奇怪结果?
对失效迭代器做解引用(*it)或者自增(it++)都属于未定义行为——C++标准完全不保证这种操作的结果。你看到输出11、7、15纯粹是巧合:
- 可能被删除的节点内存还没被操作系统回收或覆盖,所以你还能读出11这个旧值;
- 而
it++操作因为原节点的红黑树结构已经被破坏,迭代器的自增逻辑出错,导致跳到了错误的节点上,才出现了不符合预期的输出。换个编译器、操作系统甚至只是重启程序,结果都可能完全不一样。
为什么范围for循环是正常的?
范围for循环的本质是调用容器的begin()和end()方法,获取当前容器里有效、最新的迭代器,遍历的是容器现存的所有元素。此时mySet里已经没有11了,所以会输出正确的有序元素:-10、-3、1、3、4、5、7、15。
正确的做法:避免迭代器失效
如果你需要在删除元素后继续使用迭代器,应该利用erase方法的返回值——它会返回指向被删除元素后继节点的有效迭代器:
std::set<int>::iterator it = mySet.find(11); if (it != mySet.end()) { it = mySet.erase(it); // 用erase的返回值更新迭代器,此时it是有效的 } // 之后用这个it遍历就安全了 for (; it != mySet.end(); ++it) { std::cout << *it << std::endl; }
另外要记住:std::set中只有指向被删除元素的迭代器会失效,其他迭代器(比如指向7、15的)依然有效,这一点和std::vector这类连续容器的迭代器失效规则不同。
内容的提问来源于stack exchange,提问作者Programming Lover
相关产品推荐
相关产品推荐

