2008年1月28日 星期一

C++ STL - Iterator的erase



std::vector<int> v;
v.push_back(0);
std::vector<int>::iterator vi = v.begin();
while(vi != v.end())
{
if(*vi == 0)
{
v.erase(vi++);
continue;
}
++vi;
}


以上這段code在VC2003上run是正常的, 用gcc 3.2.2的話會造成segmentation fault。

很明顯的, vi++這個iterator的運算在兩個平台上有不同的implementation, gcc是先erase才做++的動作, 因為erase後iterator會指向無法預測的地方, 所以才會產生seg-fault。而VC2003則是先++才把原iterator傳進去, 所以沒有問題。

正確的寫法應該是 vi = v.erase(vi);

4 則留言:

Rick Yang 提到...

hi, code 的第二行應該是 "v.push_back(0);" :)

Fox S.C. Yang 提到...

感謝指正

fr3@K 提到...

在 gcc 與 msvc 都是先 vi++ 而後 v.erase().

先不考慮根據 C++ 標準, 對 vector 做 erase/insert 等會使所有的 iterator 失效...

在第一次 vi++ 後, vi 實際上已經等於此刻的 v.end(), 但傳遞給 v.erase() 的 iterator 仍是 vi 原本的值 (increment 之前的值, 因為是 postfix increment).

而當 v.erase() 執行後. v.end() 在這時候被往前移動了一步. 也就是說 vi 的位置超過了新的 v.end() 變成無效的 iterator 永遠不會等於 v.end(). 於是接下來的都成為 undefined behavior.

你可以把那一行 vi.erase(vi++) 改成等校的三個 statement: iterator pos = vi; ++vi; vi.erase(pos); 也會得到同樣的結果.

我手上沒有 vc2003, 因此無法得知為什麼這個程式不會馬上出錯. 用 2005 測試的結果也是會出錯, 雖然錯誤的時間與地點不同, 但也不讓人意外, 畢竟是 undefined behavior.

很顯然你已經知道正確的作法 vi = v.erase(vi). 但分析問的的推論還不是正確的. 加油~

Fox S.C. Yang 提到...

嗯 原來重點是end()這個iterator會移位
難怪我在刪除非最後一個元素的時候, 都不會發生錯誤
當初因為已經知道正確寫法, 所以就沒去深究了...

真是受教了:P