2.12、remove 和 erase 区别

1. rem)ove和erase的本质区别

remove

  • • 作用:重新排列容器中的元素,将不需要删除的元素移动到序列前部,返回指向“新末尾”的迭代器。
  • • 关键特性
    • • 不真正删除容器中的元素,也不改变容器大小。
    • • 仅将要删除的元素“覆盖”或“移动”到序列后面,容器尾部元素变为未定义状态。
  • • 依赖性:和容器类型无关,只依赖于迭代器。

erase(成员函数)

  • • 定义:是容器(如std::vectorstd::list等)提供的成员函数。
  • • 作用:真正从容器中删除元素,改变容器大小,并释放相应内存(对于动态数组容器如vector)。
  • • 参数:接受一个或一对迭代器,删除指定位置或范围内的元素。
  • • 依赖性:需要容器自身实现,删除元素后容器需调整内部结构(如移动元素、更新大小等)。

2. 为什么需要“erase-remove惯用法”?

  • • 原因remove不改变容器大小,仅将不需要的元素移动到后面,容器尾部仍保留“垃圾”元素,需调用容器的erase真正删除这些元素。
  • • 典型用法
    auto new_end = std::remove(vec.begin(), vec.end(), value_to_remove);
    vec.erase(new_end, vec.end());
    • • remove将所有不等于value_to_remove的元素移动到前面,返回新的逻辑末尾迭代器new_end
    • • erase负责真正删除从new_endvec.end()之间的元素,缩小容器大小。

3. 详细示例说明

std::vector<int> vec = {1234536};

// 只调用 remove
auto new_end = std::remove(vec.begin(), vec.end(), 3);
// vec 现在看起来像 {1, 2, 4, 5, 6, ?, ?},但 size 仍是7
std::cout << "Size after remove: " << vec.size() << std::endl; // 输出7

// 需要调用 erase 真正删除
vec.erase(new_end, vec.end());
std::cout << "Size after erase: " << vec.size() << std::endl; // 输出5

4.设计理念与性能考量

  • • 设计理念
    • • std::remove是通用算法,不依赖容器实现,负责“逻辑删除”(移动元素)。
    • • erase是容器成员函数,负责物理删除和内存管理。
  • • 性能
    • • 若只用erase多次删除元素,每次删除后都需移动后续元素,性能开销大。
    • • remove一次遍历完成元素移动,erase一次调用完成删除,整体效率更高。

5. C++20引入的std::erase和std::erase_if

  • • 作用:封装了“erase-remove惯用法”,使代码更简洁。
  • • 示例
    std::vector<int> vec = {12345};
    std::erase(vec, 3); // 直接删除所有值为3的元素

6. 总结

方面 std::remove erase(容器成员函数)
类型 算法(algorithm) 容器成员函数
功能 重新排列元素,移动不删除的元素到前面 真正删除元素,改变容器大小
是否改变大小 不改变容器大小 改变容器大小
是否释放内存 不释放内存 释放内存
依赖 仅依赖迭代器 依赖容器内部实现
典型用法 erase配合使用,形成“erase-remove惯用法” 单独调用删除指定位置或范围元素
性能 一次遍历移动元素,避免多次移动开销 多次调用可能导致多次元素移动,效率较低

本文首发于【讳疾忌医-note】公众号,未经授权,不得转载。
(加入我的知识星球,免费获取账号,解锁所有文章。)

阅读剩余
THE END