C++ 怎么删除vector元素 C++ erase函数与remove算法结合用法【容器】

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 是个“逻辑删除”算法:它把不满足条件的元素前移,返回一个新逻辑终点迭代器,但容器大小不变,后面是未定义值(通常是原尾部残留)。

典型误用:

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::liststd::deque(后者支持高效首尾删,但中部删仍慢)。

另一个易忽略点:erase 后立即访问 v.back()v[v.size()-1] 前,务必检查 v.empty(),否则越界未定义行为。

  • 释放内存?erase 不改变容量(v.capacity() 不变),要用 shrink_to_fit() 提议释放(不保证执行)
  • 多线程下,eraseremove 都非线程安全,需外部同步
  • std::unique 去重时,必须先排序,否则只删相邻重复项