vector 删除元素:erase-remove 惯用法
- 1. 问题背景
- 2. std::remove 实际做了什么
- 3. erase-remove 惯用法
- 4. C++20 的更简洁写法
- 5. 遍历时删除元素
- 6. 删除多个元素的性能
- 7. 常见误区
- 8. 一页速查
- 9. 总结
1. 问题背景
在 vector 中删除满足条件的元素,看起来像一个简单操作,但初学时很容易误解 std::remove。它的名字叫 remove,却不会真正改变容器大小。
2. std::remove 实际做了什么
std::remove 会把需要保留的元素移动到区间前部,并返回新的“逻辑结尾”。尾部仍然存在,只是内容处于未指定状态。
#include<algorithm>#include<vector>std::vector<int>values{1,0,2,0,3};autonewEnd=std::remove(values.begin(),values.end(),0);执行后 values.size() 仍然是 5。要真正缩短容器,还需要 erase。
3. erase-remove 惯用法
values.erase(std::remove(values.begin(),values.end(),0),values.end());这段代码分两步:remove 把保留元素移到前面;erase 删除从新逻辑结尾到真实结尾的元素。
按条件删除时使用 remove_if:
values.erase(std::remove_if(values.begin(),values.end(),[](intx){returnx<0;}),values.end());4. C++20 的更简洁写法
C++20 为常用容器提供了 std::erase 和 std::erase_if。
std::erase(values,0);std::erase_if(values,[](intx){returnx<0;});表达更直接,也不容易忘记第二步 erase。
5. 遍历时删除元素
需要边遍历边删除时,使用 erase 的返回值继续迭代。
for(autoit=values.begin();it!=values.end();){if(*it%2==0){it=values.erase(it);}else{++it;}}vector::erase 会让删除位置及其后的迭代器、引用和指针失效,因此不能继续使用旧 it,也不要在 erase 后无条件再 ++it。
6. 删除多个元素的性能
循环中反复调用 erase 可能多次移动后续元素,最坏情况下成本很高。如果删除条件可以一次判断,erase-remove 或 erase_if 通常更高效,因为保留元素只需集中移动。
如果不要求保持原顺序,还可以用“与末尾交换再 pop_back”的方式进行 O(1) 单次删除:
values[index]=std::move(values.back());values.pop_back();这种方法会改变元素顺序,只适合顺序不重要的场景。
7. 常见误区
- 只调用 remove,误以为 size 会变小;
- erase 后继续使用已经失效的迭代器;
- 在范围 for 中直接删除 vector 元素;
- 不考虑是否需要保持元素原顺序。
8. 一页速查
| 场景 | 推荐写法 |
|---|---|
| 删除指定值(C++11) | erase(remove(…), end()) |
| 按条件删除(C++11) | erase(remove_if(…), end()) |
| 删除指定值(C++20) | std::erase(container, value) |
| 按条件删除(C++20) | std::erase_if(container, pred) |
| 遍历中删除 | it = container.erase(it) |
9. 总结
std::remove 只调整元素位置,不负责缩小容器;真正删除要配合 erase。C++20 可优先使用 std::erase 和 std::erase_if。遍历中删除时必须接住 erase 返回的新迭代器,并牢记 vector 的迭代器失效规则。