不能直接用for循环+erase删除元素,因为erase会使迭代器失效并导致跳过元素或崩溃;应配对使用remove_if(搬移保留元素)和erase(截断冗余内存),否则容器大小不变且末尾残留旧值。

为什么不能用for循环+erase删满足条件的元素
直接写 for (auto it = v.begin(); it != v.end(); ++it) 然后在循环体里调 v.erase(it),会导致迭代器失效、跳过下一个元素,甚至崩溃。因为 erase() 会移动后续所有元素,++it 会越过本该检查的位置;更糟的是,it 被擦除后变成悬空迭代器,再自增就是未定义行为。
用erase + remove_if删除满足条件的元素
remove_if 不真正删数据,只把“要留下的”往前搬,返回新逻辑结尾的迭代器;erase 再一次性截掉后面那段冗余内存。这才是安全又快的做法。
- 必须配对使用:单独调
remove_if后不接erase,容器大小不变,但末尾残留旧值(调试时看到奇怪数字,八成是这步漏了) - lambda 条件写在
remove_if第三个参数里,比如删所有负数:[](int x) { return x - 注意函数对象或 lambda 捕获变量时生命周期——别捕获局部地址或已析构对象的引用
- 性能上,它只遍历一次,移动是
memmove级别;而反复erase是 O(n²),数据一多就明显卡顿
删第一个匹配元素,别硬套remove_if
如果只要删首个满足条件的元素,用 remove_if 是杀鸡用牛刀。它会扫完整个容器,哪怕早找到了也停不下来。
- 改用
std::find_if定位,再单次erase - 记得判空:
if (it != v.end()) v.erase(it);,否则对v.end()调erase是未定义行为 - 如果条件复杂、涉及状态计算,
find_if的 predicate 和remove_if的其实写法一致,复用即可
erase-remove惯用法不适用于list或set
remove 和 remove_if 依赖随机访问和连续内存搬移,所以只对 std::vector、std::deque、std::string 有效。对 std::list,它自带 remove_if() 成员函数,内部用指针重连,O(n) 且不拷贝元素;对 std::set 这类关联容器,得用 erase 配合 find 或范围循环,没法用 remove 系列。
最容易被忽略的一点:删完别以为万事大吉——remove_if 返回的迭代器位置必须原样传给 erase,中间不能做任何可能使迭代器失效的操作(比如再插入、再删别的位置、调用 reserve 或 shrink_to_fit)。两步之间,容器状态必须保持稳定。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











