正确做法是让 it 接收 erase 的返回值:for (auto it = vec.begin(); it != vec.end(); ) { if (should_remove(*it)) it = vec.erase(it); else ++it; },避免迭代器失效;或用 erase-remove 惯用法提升性能。

用 erase 配合返回的迭代器继续遍历
直接在 for 循环里调用 vec.erase(it) 后还执行 ++it,会导致迭代器失效并越界——因为 erase 返回的是**下一个有效位置**,不是原地递增后的地址。
正确做法是让 it 接收 erase 的返回值:
for (auto it = vec.begin(); it != vec.end(); ) {
if (should_remove(*it)) {
it = vec.erase(it); // erase 返回下一个有效迭代器
} else {
++it;
}
}
- 这是最通用、最安全的方式,适用于所有需要条件删除的场景
- 不要写成
vec.erase(it++);—— 这会先用失效的it做参数,再自增,行为未定义 - 注意:
erase对vector是 O(n) 操作,频繁删除时性能差
用 std::remove_if + erase(推荐用于批量删除)
std::remove_if 不是真的删,而是把要保留的元素往前挪,返回新逻辑尾部;再用 erase 一次性切掉后面那段——这叫“erase–remove 惯用法”,避免了多次内存搬移。
vec.erase(
std::remove_if(vec.begin(), vec.end(), [](const auto& x) {
return x % 2 == 0; // 删除偶数
}),
vec.end()
);
- 比手写循环快,尤其删多个元素时
- 只适用「判断条件明确、不依赖外部状态变更」的场景
- 注意:不能用它来处理「边删边改容器其他部分」的逻辑,比如删除某个元素后要立刻修改后续某元素的值
为什么不能用普通 for (size_t i = 0; i ?
当 i 指向的元素被删掉后,后面所有元素前移,但 i 仍自增,结果会跳过紧邻的下一个元素。
立即学习“C++免费学习笔记(深入)”;
例如 {1,2,3,4} 中删所有偶数:
删掉 2 后变成 {1,3,4},i 变成 2,此时访问 vec[2] 是 4,3 就被跳过了。
- 如果非要下标遍历,得在删除后手动
--i - 但这样容易漏逻辑、难维护,不如用迭代器方案直观
-
vector::size()在循环中反复调用没问题,但要注意它返回size_t,和负数比较会出隐式转换问题
哪些情况会触发迭代器彻底失效?
vector 的迭代器在以下操作后**全部失效**:push_back(引起扩容)、insert(在非尾部)、clear、以及任何导致内部存储重分配的 erase(其实只有扩容才重分配,单次 erase 不会)。
- 所以别在
erase后还拿着旧迭代器去解引用或比较 - 多线程环境下,即使只读也需注意:一个线程
erase,另一个线程还在用迭代器,就是 UB - 用
std::vector::data()获取裸指针时,同样受扩容影响——它本质就是&vec[0]










