在使用vector的過程中,有時會遇到需要循環遍歷vector,並刪除符合指定條件的元素。
當“指定條件”不復雜時,應該盡量使用erase(remove_if(begin, end, func), end)的形式來完成功能。
但有時候“指定條件”過於復雜,不得不顯式地寫一個for循環來處理。我們必須小心在意erase所帶來的side effect,一個一般性的for循環如下:
1 for (std::vector<int>::iterator it = intVec.begin(); it != intVec.end(); /**/) 2 { 3 if (*it == 3) 4 { 5 intVec.erase(it); 6 } 7 else 8 { 9 ++it; 10 } 11 }
所要注意的是it = intVec.erase(it)。實際上這里如果寫成intVec.erase(it),即不對it做重新賦值,代碼也能正常執行,特別是release版本幾乎所有的編譯器編譯后都能產生結果正確的代碼。而debug模式下有一些較新的編譯器會在編譯時給出警告,並在運行時出現斷言錯誤。
為什么一個錯誤的寫法在大多數情況下都能得到正確的答案?
根據STL的描述,執行erase(it)后,it和it之后的迭代器都可能會失效。這一點很好理解。因為vector一般由動態數組實現,它的元素在內存中是連續存儲的。當刪除掉it所指向元素時,原本在it后面的元素需要集體前移。迭代器本身幾乎可以理解為是一個指針,在erase之后它所指向的位置並沒有變化,只是那個位置的元素發生了變化,而且恰好變成了我們所想要的。至少大多數STL版本是這么實現的,因為這處理起來比較自然。
然而我們不能依賴於這個一般性事實,而應該采用it=intVec.erase(it)的形式來對it重新賦值。STL中有要求vector的erase函數要返回指向被erase的迭代器的下一個位置,寫成it=intVec.erase(it)是萬無一失的,而寫成intVec.erase(it)雖然實際可行,但是具有潛在風險,萬一某一天erase會影響it的指向(STL只要求erase移除元素,而沒有保證it自身不變),程序就極有可能出問題。
根據標准所描述的約束來編程,而不是根據具體的實現細節來編程。
對於C++ STL,似乎有很多個版本的實現,而它們或多或少都有所偏差。這里有兩個網站,可以進行參考:http://www.cplusplus.com/reference/和http://www.sgi.com/tech/stl/。
