2011-12-21 167 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