17370845950

C++ vector erase迭代器失效 C++删除元素后的正确遍历写法【避坑】
erase后迭代器必然失效,正确做法是用其返回值续接(如it = v.erase(it))或采用remove-erase惯用法;反向遍历或调整索引也可避免失效问题。

erase之后的迭代器一定失效,不能++再用

调用 vector::erase 删除元素后,被删位置及之后所有迭代器全部失效。常见错误是写成:for (auto it = v.begin(); it != v.end(); ++it) { if (...) v.erase(it); }——这会导致 it 失效后又被 ++it 解引用,触发未定义行为(UB),多数情况直接崩溃或静默错乱。

根本原因是 vector 内部是连续内存,删除中间元素会引发后续元素前移,原 it 指向的地址已不属于该容器,更别说自增操作了。

用erase返回值续接迭代器(最常用且安全)

vector::erase 返回**删除位置之后的第一个有效迭代器**(C++11起),正好可用来跳过已处理位置,避免失效问题。

  • 删除满足条件的单个元素:it = v.erase(it); —— 此时 it 已指向下一个有效位置,不要++it
  • 配合 while 遍历删除多个元素:auto it = v.begin(); while (it != v.end()) { if (condition) it = v.erase(it); else ++it; }
  • 注意:如果用 for 循环,必须把 ++it 放在 else 分支里,或统一用 it = v.erase(it) 后不自增

用remove-erase惯用法批量删除(推荐用于条件过滤)

当要按值或谓词批量删除时,std::remove + erase 组合更高效、更简洁,且完全规避迭代器管理问题:

v.erase(std::remove(v.begin(), v.end(), 42), v.end()); // 删除所有值为42的元素
v.erase(std::remove_if(v.begin(), v.end(), [](int x) { return x < 0; }), v.end()); // 删除所有负数

原理:std::remove 不真正删

除,而是将保留元素前移,并返回新逻辑结尾;erase 再一次性擦除尾部冗余区间。整个过程只遍历一次,无迭代器失效风险。

⚠️ 注意:remove 是算法,不适用于 listdeque 的“高效删除”场景(它们有成员函数 remove),但对 vector 来说,这是最稳的选择。

用索引遍历替代迭代器(简单场景够用)

如果只是简单判断删除,且不需要访问前后元素,用下标反而更直观、不易出错:

for (int i = v.size() - 1; i >= 0; --i) { // 从后往前删,避免下标偏移
    if (v[i] % 2 == 0) v.erase(v.begin() + i);
}

或者正向遍历时动态调整上界:for (int i = 0; i —— 这和迭代器版的 while 逻辑一致,只是换种写法。

索引方式在小数据量、逻辑简单时可读性高;但要注意 v.size() 是 O(1),频繁调用无性能压力,别担心。

真正容易忽略的是:即使只删一个元素,只要用了 erase,它后面所有迭代器就全废了——包括你刚存下来的 end()。所以别缓存 v.end() 在循环条件里,也别在 erase 后还拿旧 it 做比较或解引用。