vector::erase 删除单个或连续元素时应避免边遍历边用递增下标删除,正确做法是使用迭代器删除后接收返回值,或逆向遍历、标记+erase-remove惯用法。

vector::erase 删除单个或连续元素时的正确姿势
直接调用 erase 是最直观的删除方式,但它会移动后续所有元素,时间复杂度是 O(n)。删末尾最快,删开头或中间代价高。
常见错误是边遍历边 erase 还用递增下标,导致跳过下一个元素:
for (size_t i = 0; i < v.size(); ++i) {
if (v[i] == 42) v.erase(v.begin() + i); // ❌ 错!i 自增后指向原 i+2 位置
}
正确做法是使用返回的迭代器(erase 返回删除位置之后的合法迭代器):
for (auto it = v.begin(); it != v.end(); ) {
if (*it == 42) it = v.erase(it); // ✅ 返回新 it,不自增
else ++it;
}
-
erase(pos)删除单个元素,返回后继迭代器 -
erase(first, last)删除区间,返回last - 删除后所有指向被删元素及之后的迭代器、引用、指针全部失效
为什么不能只用 std::remove?它根本没删元素
std::remove 是个“逻辑删除”算法:它把不满足条件的元素前移,返回一个新逻辑终点迭代器,但容器大小不变,后面是未定义值(通常是原尾部残留)。
立即学习“C++免费学习笔记(深入)”;
典型误用:
std::remove(v.begin(), v.end(), 42); // ❌ 容器 size 没变,数据没真删
// 此时 v 可能变成 {1,2,3,1,2,3,?, ?, ?},? 是脏数据
必须配合 erase 才算真正删除(即“erase–remove 惯用法”):
v.erase(std::remove(v.begin(), v.end(), 42), v.end()); // ✅ 真删
-
std::remove是稳定算法,保持剩余元素相对顺序 - 只适用于可赋值类型,且比较用
== - 对
vector来说,它比多次erase快得多(一次移动,O(n);多次 erase 是 O(n²))
删除满足任意条件的元素:用 remove_if + lambda
当删除逻辑不是简单相等(比如删偶数、删空字符串、删超时对象),就得用 std::remove_if:
v.erase(std::remove_if(v.begin(), v.end(), [](int x) { return x % 2 == 0; }), v.end());
注意 lambda 捕获和生命周期:如果要访问外部变量(如阈值、容器引用),需显式捕获,避免悬垂引用。
- lambda 中不要修改容器本身,否则行为未定义
- 若条件判断开销大,可提前计算缓存,避免重复调用
- 对自定义类型,确保
operator==或谓词逻辑正确,尤其注意 const 成员函数修饰
性能与安全边界:vector 删除的隐藏成本
频繁在头部或中部删除,vector 不是最佳选择——每次 erase 都触发大量内存拷贝。此时应考虑 std::list 或 std::deque(后者支持高效首尾删,但中部删仍慢)。
另一个易忽略点:erase 后立即访问 v.back() 或 v[v.size()-1] 前,务必检查 v.empty(),否则越界未定义行为。
- 释放内存?
erase不改变容量(v.capacity()不变),要用shrink_to_fit()提议释放(不保证执行) - 多线程下,
erase和remove都非线程安全,需外部同步 - 用
std::unique去重时,必须先排序,否则只删相邻重复项










