C++ 中删除 vector 元素,关键在于先明确删除语义:是删除单个指定值、删除所有匹配值,还是按条件删除;同时要保证迭代器失效后不继续使用旧迭代器。正确做法通常是用 erase 配合 remove / remove_if 完成批量删除,或对单元素删除使用 erase 并接收返回的新迭代器。若在循环中删除,必须用 it = vec.erase(it) 的写法,而不是 vec.erase(it); ++it;。
删除 vector 元素的核心判断是:批量删除用 erase-remove 惯用法,单元素删除用 erase 并接住返回值,循环删除时不要对已失效迭代器做自增。需要保持元素顺序时不要用 swap-and-pop;需要连续内存且删除频繁时,应重新评估是否该用 list 或其它容器。
先分清三种删除场景
vector 删除元素不是单一操作,按目标可以分成三类:删除一个已知位置的元素、删除所有等于某个值的元素、删除满足条件的元素。三种场景对应不同的代码写法,混用容易写出未定义行为。
- 删除单个位置:用
vec.erase(vec.begin() + index),erase 返回被删元素之后的新迭代器。如果后续还要用迭代器,必须接收返回值。 - 删除所有指定值:用
vec.erase(std::remove(vec.begin(), vec.end(), value), vec.end())。remove 只是把不删除的元素前移,返回新逻辑结尾,erase 才真正缩短容器。 - 删除满足条件的元素:用
vec.erase(std::remove_if(vec.begin(), vec.end(), pred), vec.end()),pred 是返回 bool 的谓词。
这三种写法的共同点是:erase 之后,指向被删位置及其之后的迭代器、指针、引用都会失效。区别在于批量删除时 remove 已经完成了元素搬移,erase 只需截断尾部,效率更可控。
循环中删除元素:用 erase 返回值推进迭代器
最常见的错误是在 for 循环里直接 vec.erase(it) 然后 ++it。erase 之后 it 已经失效,再自增是未定义行为。正确写法是把 erase 的返回值赋给 it,并且不在同一次循环里额外自增。
for (auto it = vec.begin(); it != vec.end(); ) {
if (should_remove(*it)) {
it = vec.erase(it); // 用返回值推进
} else {
++it;
}
}
如果删除条件可以用谓词表达,优先用 remove_if 加 erase,代码更短,也避免手写循环时漏掉迭代器推进。注意 remove_if 不保证保留元素的相对顺序吗?实际上 std::remove_if 保持未删除元素的相对顺序,但会搬移元素;如果谓词有副作用,需要确认副作用只依赖元素值,不依赖元素地址。
不想保持顺序时,可以用 swap-and-pop
如果删除单个元素且不要求保持剩余元素的顺序,可以把待删元素与最后一个元素交换,然后 pop_back。这种写法避免搬移中间元素,但会改变顺序,且需要先确认待删位置不是最后一个元素。
void erase_unordered(std::vector<int>& vec, size_t index) {
if (index >= vec.size()) return;
vec[index] = std::move(vec.back());
vec.pop_back();
}
swap-and-pop 适合元素较大、删除位置随机、且顺序无意义的场景。如果业务依赖 vector 中元素的原始次序,不要用这种写法。另外,swap-and-pop 之后,原来指向最后一个元素的迭代器、指针、引用也会失效,需要一并检查。
删除后容量不会自动缩减
erase 只减少 size,不减少 capacity。如果删除大量元素后希望释放多余内存,可以用 vec.shrink_to_fit(),但这是非强制请求,标准不保证一定回收。更稳妥的做法是 swap 一个空 vector 或重新构造,但会丢失原有 capacity,后续插入可能重新分配。
如果删除操作非常频繁,且每次只删少量元素,vector 的连续内存搬移成本会累积。此时应评估是否改用 std::list 或 std::unordered_set 等容器。但不要仅因为“删除多”就换容器,先确认删除频率、元素大小和是否必须随机访问,再做取舍。
常见问题
删除元素后,原来的迭代器还能用吗?
不能。erase 会使指向被删位置及其之后的所有迭代器、指针、引用失效。批量删除时,remove 阶段不会使迭代器失效,但随后的 erase 会使从新逻辑结尾到原 end 的迭代器失效。循环中必须用 erase 的返回值更新迭代器。
remove 和 erase 有什么区别?
std::remove 是算法,只重新排列元素并返回新逻辑结尾,不改变容器大小;vector::erase 是成员函数,真正删除元素并缩短 size。erase-remove 惯用法把两者结合:remove 把要保留的元素前移,erase 删除尾部多余元素。