在應用中,我們通常不可避免地要對容器中的某些特定元素進行刪除操作。這看起來並不是什麼困難的問題。我們先寫一個迴圈來迭代容器中的元素,如果迭代元素是要刪除的元素,則刪除之。代碼如下所示:
vector<int> intContainer;for(vector<int>::iterator is = intContainer.begin(); it != intContainer.end(); ++it){if ( *it == 25) intContainer.erase(it);}
寫出此代碼的原意是將vector中值為25的元素刪除,但不幸的是,這樣做是錯誤的,這麼做會帶來詭異的未定義行為。因為當一個容器的一個元素被刪除時,指向那個元素的所有迭代器將失效。當intContainer.erase(it)返回時,it已經失效。在for迴圈中對於失效的it執行自增操作,這是一件多麼不靠譜的事情啊。
既然這樣行不通,那麼我們可以求助於STL提供的remove演算法。藉助remove演算法來達到刪除元素的目的。
vector<int> intContainer;size_t before_size = intContainer.size();remove(intContainer.begin(), intContainer.end(), 25);size_t after_size = intContainer.size();
運行程式以後發現before_size和after_size是一樣的,說明元素並沒有被真正刪除。寫出以上程式,是處於對remove演算法的不瞭解而致。STL中remove演算法會將不該刪除的元素前移,然後返回一個迭代器,該迭代器指向的是那個應該刪除的元素,僅此而已。所以如果要真正刪除這一元素,在調用remove之後還必須調用erase,這就是STL容器元素刪除的"erase_remove"的慣用法。
vector<int> intContainer;intContainer.erase( remove(intContainer.begin(), intContainer.end(), 25), intContainer.end());
erase-remove的慣用法適用於連續記憶體容器,比如vector,deque和string,它也同樣適用於list,但是並不是我們推薦的方法,因為使用list成員函數remove會更高效,代碼如下:
list<int> list_int;....list_int.remove(25);
如果是關聯容器呢?標準關聯容器沒有remove成員函數,使用STL演算法的remove函數時編譯同不過。所以上述remove形式對於標準關聯容器並不適用。在這種情況下,解決辦法就是調用erase:
map<int, int> mapContainer;...mapContainer.erase(25);
對於標準關聯容器,這樣的元素刪除方式是簡單有效,時間複雜度為O(logn).
當我們需要刪除的不是某一個元素,而是具備某一條件的元素的時候,我們只需要將remove替換成remove_if即可
bool Is2BeRemove(int value){return value < 25;}vector<int> nVec;list<int> nList;....nVec.erase(remove_if(nVec.begin(), nVec.end(), Is2BeRemove), nVec.end());nList.remove_if(Is2BeRemove);
總結如下
刪除容器中具有特定值的元素:
如果容器是ector、string或者deque,使用erase-remove的慣用法。如果容器是list,使用list::remove。如果容器是標準關聯容器,使用它的erase成員函數。
刪除容器中滿足某些條件的元素:
如果容器是ector、string或者deque,使用erase-remove_if的慣用法。如果容器是list,使用list::remove_if。如果容器是標準關聯容器,使用remove_copy_if & swap 組合演算法,或者自己協議個遍曆刪除演算法。
參考資料:李健《編寫高品質C++代碼》第七章,用好STL這個大輪子