2011-12-21 79 views
16

我正在循环一个带有循环的向量,如for(int i = 0; i < vec.size(); i++)。在这个循环中,我检查该向量索引处元素的条件,如果某个条件为真,我想删除该元素。如何在循环播放时从矢量中删除元素?

如何删除一个向量元素,同时循环而不会崩溃?

+4

你可以使用'remove_if'和'erase'呢? http://en.wikipedia.org/wiki/Erase-remove_idiom – msandiford 2011-12-21 22:30:27

+0

删除删除:http://stackoverflow.com/questions/4175896/safe-way-to-continuously-erase-from-a-stdvector – 2011-12-21 23:01:57

回答

31

从满足给定谓词的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()); 

如果你坚持使用指标,你不应该增加索引对于每一个元素,但只针对那些没有得到删除:

std::vector<std::string>::size_type i = 0; 
while (i < v.size()) { 
    if (shouldBeRemoved(v[i])) { 
     v.erase(v.begin() + i); 
    } else { 
     ++i; 
    } 
} 

然而,这不仅是更多的代码和更少的习惯(读:C++程序员实际上得看代码,而“删除&删除'成语立即给出一些想法发生了什么),但也是效率低得多,因为矢量存储eir元素存储在一个连续的内存块中,因此擦除除矢量末尾以外的位置也会将该段擦除后的所有元素移动到新的位置。

+1

如何知道在v.erase()之后有多少元素被杀死? (不计算前后的大小?) – dynamic 2013-06-03 09:59:26

+0

谓词不一定是函数,但可能是一个函数(特别是如果您需要获取外部数据来决定是否删除元素)。 – 2014-09-29 16:25:48

+1

@dynamic'std :: vector'具有随机访问迭代器的功能,所以你可以简单地通过从当前结束迭代器中减去'std :: remove_if'返回的新结束迭代器来判断有多少元素被擦除(例如'v.end ()')。 – 2015-03-09 21:38:59

1

向后迭代向量。这样,你就不会破坏你尚未访问的元素的能力。

8

使用Erase-Remove Idiom,使用remove_if与谓词来指定您的条件。

+0

我们可以保留SO的联系实习生。这里不太可能被破坏。 – 2011-12-21 23:02:48

9

如果您不能使用删除/擦除(例如,因为你不希望使用lambda表达式或写一个谓语),使用序列容器元素移除标准成语:

for (auto it = v.cbegin(); it != v.cend() /* not hoisted */; /* no increment */) 
{ 
    if (delete_condition) 
    { 
     it = v.erase(it); 
    } 
    else 
    { 
     ++it; 
    } 
} 

如果可能的话,虽然,宁愿删除/擦除:

#include <algorithm> 

v.erase(std::remove_if(v.begin(), v.end(), 
         [](T const & x) -> bool { /* decide */ }), 
     v.end()); 
+0

'for(auto it ...'会编译吗? – ThomasMcLeod 2011-12-22 04:17:26

+0

对于C++ 11编译器,是的 – Vortico 2012-08-07 12:25:49

1

我意识到你是有关从载体移除具体要求,但只是想指出,这是昂贵的,从一个std删除项目::向量因为拆除的项目后,所有项目必须被复制到新的位置。如果你打算从容器中移除项目,你应该使用std :: list。 std :: list :: erase(item)方法甚至会返回指向刚刚被擦除之后的值的迭代器,所以在for或while循环中使用它很容易。 std :: list的好处在于指向未擦除项目的迭代器在整个列表中保持有效。参见例如docs at cplusplus.com。这就是说,如果你没有选择,一个可以工作的技巧就是创建一个新的空向量,并从第一个向量向它添加项目,然后使用std :: swap(oldVec,newVec),这是非常高效(无需复制,只需更改内部指针)。

3
if(vector_name.empty() == false) { 
    for(int i = vector_name.size() - 1; i >= 0; i--) 
    { 
     if(condition) 
      vector_name.erase(vector_name.at(i)); 
    } 
} 

这对我有用。并且不需要考虑索引已经被擦除。

+0

应该是erase(vector_name.begin()+ i);还要检查vector是否为空是无用的,因为size = = 0会导致在for循环中没有迭代 – log0 2015-12-14 11:50:45

+0

@ log0随机访问是可能的为什么不能?并且同意后者 – 2015-12-28 08:40:18

+0

'erase'以迭代器作为参数,'at'返回向量**值**(例如一个字符串) – log0 2015-12-29 15:32:43