1. 程式人生 > >STL容器刪除元素的陷阱

STL容器刪除元素的陷阱

今天看Scott Meyers大師的stl的用法,看到了我前段時間犯的一個錯誤,發現我寫的程式碼和他提到錯誤程式碼幾乎一模一樣,有關stl容器刪除元素的問題,錯誤的程式碼如下:
std::vector<struct> mFriendList;
...
std::vector<struct>::iterator iter = mFriendList.begin();
for ( ; iter != mFriendList.end(); ++iter)
{
    if (...)
        mFriendList.erase(iter);
}
記得當時Once給我說過這個問題,還給我改過程式碼,我當時不明白為什麼,只知道程式執行的時候如果if為true那麼程式就肯定會崩潰。
大師的說法是:當容易中的一個元素被刪除時,指向該元素的所有迭代器都變得無效。上面的程式碼中,只要執行了erase(iter),那麼iter就會變得無效,那麼執行++iter就肯定會出錯。

在網上看到有人總結如下兩條:
1. 對於節點式容器(map, list, set)元素的刪除,插入操作會導致指向該元素的迭代器失效,其他元素迭代器不受影響
2. 對於順序式容器(vector,string,deque)元素的刪除、插入操作會導致指向該元素以及後面的元素的迭代器失效

總結了一下,並回想Once當時給我改的程式碼,所以正確的寫法應該是這樣的:
1.對於節點式容器
std::list<struct> mList;
...
std::list<struct>::iterator iter = mList.begin();
for ( ; iter != mList.end(); )
{
    if (...)
    {
        //因為節點式只會導致當前節點迭代器失效,所以刪除節點的同時對迭代器進行後移的操作,因為其他元素不會失效
        mList.erase(iter++);
    }
    else
    {
        ++iter;
    }
}

2.對於順序式容器
std::vector<struct> mVector;
...
std::vector<struct>::iterator iter = mVector.begin();
for ( ; iter != mVector.end(); )
{
    if (...)
    {
        //這裡就比較有說法了,因為順序式容器會使本身和後面的元素迭代器都失效,所以不能簡單的++操作
        //這裡順序式容器的erase()會返回緊隨被刪除元素的下一個元素的有效迭代器
        //而節點式容器的erase()的返回值是void,這點我感覺太神奇了,確實太神奇了!!!!
        iter = mVector.erase(iter);
    }
    else
    {
        ++iter;
    }
}

注意:容器看具體STL庫的實現了,VS中兩類容器的earse都返回下一