C++ 如何在循环时从向量中删除元素?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8597240/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to delete an element from a vector while looping over it?
提问by zebra
I am looping through a vector with a loop such as for(int i = 0; i < vec.size(); i++)
. Within this loop, I check a condition on the element at that vector index, and if a certain condition is true, I want to delete that element.
我正在循环遍历一个带有循环的向量,例如for(int i = 0; i < vec.size(); i++)
. 在这个循环中,我检查该向量索引处元素的条件,如果某个条件为真,我想删除该元素。
How do I delete a vector element while looping over it without crashing?
如何在循环时删除矢量元素而不会崩溃?
回答by Frerich Raabe
The idiomatic way to remove all elements from an STL container which satisfy a given predicate is to use the remove-erase idiom. The idea is to move the predicate (that's the function which yields true or false for some element) into a given function, say pred
and then:
从 STL 容器中删除满足给定谓词的所有元素的惯用方法是使用remove-erase idiom。这个想法是将谓词(即对某些元素产生真或假的函数)移动到给定的函数中,pred
然后说:
static bool pred( const std::string &s ) {
// ...
}
std::vector<std::string> v;
v.erase( std::remove_if( v.begin(), v.end(), pred ), v.end() );
If you insist on using indices, you should not increment the index for every element, but only for those which didn't get removed:
如果您坚持使用索引,则不应为每个元素增加索引,而只为那些没有被删除的元素增加索引:
std::vector<std::string>::size_type i = 0;
while ( i < v.size() ) {
if ( shouldBeRemoved( v[i] ) ) {
v.erase( v.begin() + i );
} else {
++i;
}
}
However, this is not only more code and less idiomatic (read: C++ programmers actually have to look at the code whereas the 'erase & remove' idiom immediately gives some idea what's going on), but also much less efficient because vectors store their elements in one contiguous block of memory, so erasing on positions other than the vector end also moves all the elements after the segment erased to their new positions.
然而,这不仅是更多的代码和更少的惯用语(阅读:C++ 程序员实际上必须查看代码,而“擦除和删除”习语会立即给出一些想法),而且效率也低得多,因为向量存储它们的元素在一个连续的内存块中,因此擦除向量末尾以外的位置也会将擦除段后的所有元素移动到新位置。
回答by Kerrek SB
If you cannot use remove/erase (e.g. because you don't want to use lambdas or write a predicate), use the standard idiom for sequence container element removal:
如果您不能使用 remove/erase(例如,因为您不想使用 lambdas 或编写谓词),请使用标准习语来删除序列容器元素:
for (auto it = v.cbegin(); it != v.cend() /* not hoisted */; /* no increment */)
{
if (delete_condition)
{
it = v.erase(it);
}
else
{
++it;
}
}
If possible, though, prefer remove/erase:
但是,如果可能的话,更喜欢删除/擦除:
#include <algorithm>
v.erase(std::remove_if(v.begin(), v.end(),
[](T const & x) -> bool { /* decide */ }),
v.end());
回答by Fred Larson
Use the Erase-Remove Idiom, using remove_if
with a predicate to specify your condition.
使用Erase-Remove Idiom,remove_if
与谓词一起使用来指定您的条件。
回答by Dawoon Yi
if(vector_name.empty() == false) {
for(int i = vector_name.size() - 1; i >= 0; i--)
{
if(condition)
vector_name.erase(vector_name.at(i));
}
}
This works for me. And Don't need to think about indexes have already erased.
这对我有用。并且不需要考虑索引已经擦除。
回答by Oliver
I realize you are asking specifically about removing from vector, but just wanted to point out that it is costly to remove items from a std::vector since all items after the removed item must be copied to new location. If you are going to remove items from the container you should use a std::list. The std::list::erase(item) method even returns the iterator pointing to the value after the one just erased, so it's easy to use in a for or while loop. Nice thing too with std::list is that iterators pointing to non-erased items remain valid throughout list existence. See for instance the docs at cplusplus.com.
我意识到您是专门询问从向量中删除的问题,但只是想指出从 std::vector 中删除项目的成本很高,因为删除项目之后的所有项目都必须复制到新位置。如果要从容器中删除项目,则应使用 std::list。std::list::erase(item) 方法甚至返回指向刚刚擦除后的值的迭代器,因此它很容易在 for 或 while 循环中使用。使用 std::list 的好处是,指向未擦除项的迭代器在整个列表存在期间保持有效。例如,请参阅cplusplus.com 上的文档。
That said, if you have no choice, a trick that can work is simply to create a new empty vector and add items to it from the first vector, then use std::swap(oldVec, newVec), which is very efficient (no copy, just changes internal pointers).
也就是说,如果你别无选择,一个可行的技巧就是创建一个新的空向量并从第一个向量向其中添加项,然后使用 std::swap(oldVec, newVec),这是非常有效的(没有复制,只是改变内部指针)。
回答by That Chuck Guy
Iterate over the vector backwards. That way, you don't nuke the ability to get to the elements you haven't visited yet.
向后迭代向量。这样,您就不会削弱访问尚未访问过的元素的能力。