迭代器失效问题
一、针对 reserve、insert、erase 三种操作引发的迭代器失效问题。
以erase为例:
因为erase后,内存重新分配,原有内存释放!所以,不能再使用原有的迭代器!
正确做法:
it = container.erase(it);//左侧it是返回的新迭代器
=》用erase的返回值(更新后的迭代器iter):指向新元素的迭代器!
void vector_iterator_invalidation() {
cout << "=== vector迭代器失效 ===" << endl;
vector<int> vec = {1, 2, 3, 4, 5, 6, 7, 8};
// ❌ 错误示例:直接删除导致迭代器失效
/*
for (auto it = vec.begin(); it != vec.end(); ++it) {
if (*it % 2 == 0) {
vec.erase(it); // ❌ it立即失效!后续++it是“未定义行为”
//注意:一旦删除,后续就不能再使用迭代器“it”!!
}
}
*/
// ✅ 正确做法1:利用erase返回值
cout << "删除前: ";
for (int val : vec) cout << val << " ";
cout << endl;
for (auto it = vec.begin(); it != vec.end(); ) {
if (*it % 2 == 0) {
it = vec.erase(it); // ✅ erase返回下一个有效迭代器
//注意:erase的返回值时“新内存空间”的迭代器,可以放心使用!
} else {
++it;
}
}
cout << "删除后: ";
for (int val : vec) cout << val << " ";
cout << endl;
}
void vector_multiple_erase() {
cout << "=== vector批量删除 ===" << endl;
vector<int> vec = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// ✅ 正确做法2:remove-erase惯用法
vec.erase(
remove_if(vec.begin(), vec.end(),
[](int x) { return x % 2 == 0; }),
vec.end()
);
cout << "remove_if后: ";
for (int val : vec) cout << val << " ";
cout << endl;
}
二、失效规则总结表
| 容器类型 | 删除操作 | 哪些迭代器失效 | 解决方案:使用返回值 |
|---|---|---|---|
| vector | erase | 被删元素及之后的所有迭代器 | 使用it = erase(it) |
| deque | erase | 所有迭代器可能失效 | 使用it = erase(it) |
| list | erase | 只有被删元素的迭代器 | 使用it = erase(it) |
| forward_list | erase_after | 只有被删元素的迭代器 | 使用返回值 |
| map/set | erase | 只有被删元素的迭代器 | 使用it = erase(it) |
| unordered_map/set | erase | 所有迭代器可能失效 | 使用it = erase(it) |
关键要点总结1
-
vector/deque:最严格,删除元素后所有后续迭代器都失效
-
list/forward_list:最宽松,只有被删元素迭代器失效
-
关联容器:中等,只有被删元素迭代器失效
-
无序容器:类似vector,可能所有迭代器都失效
三、总结2
| 操作类型 | 失效原因 | 解决方案 |
|---|---|---|
| reserve | 内存重分配导致指针变化 | 预存有效元素数量 |
| insert | 扩容后迭代器定位错误 | 记录相对偏移量 |
| erase | 元素移动导致内容改变 | 使用返回的迭代器 |
四、解决方法:
开发建议:
- 避免在可能触发扩容的操作后直接使用原有迭代器;
- 优先使用标准库
erase的返回值(新迭代器); - 在性能敏感场景,提前计算容量避免频繁扩容;
- 自定义容器时,严格遵循迭代器失效规则;
黄金法则:
-
使用
it = container.erase(it)而不是直接container.erase(it++); -
对于序列容器,优先考虑remove-erase惯用法;
-
在循环中删除时,永远不要保存可能失效的迭代器;
掌握这些规则可以避免90%的迭代器失效问题!
