在遍歷中使用 iterator/reverse_iterator 進行 Erase 的用法

在遍歷中使用 iterator/reverse_iterator 進行 Erase 的用法
本文遵循“署名-非商業用途-保持一致”創作公用協議

衆所周知,在使用迭代器遍歷 STL 容器時,需要特別留意是否在循環中修改了迭代器而導致迭代器失效的情形。下面我來總結一下在對各種容器進行正向和反向遍歷過程中刪除元素時,正確更新迭代器的用法。本文源碼:https://code.csdn.net/snippets/173595

首先,要明白使用正向迭代器(iterator)進行反向遍歷是錯誤的用法,要不幹嘛要有反向迭代器呢(reverse_iterator)。其次,根據容器的特性,遍歷刪除操作的用法可以分爲兩組,第一組是 list 和 vector,第二組是 map 和 set。


接下來,看看具體怎麼個用法。

第一種情形:正向遍歷刪除元素

對 list 和 vector 來說,它們的 erase 函數會返回下一個迭代器,因此在遍歷時,只需要 it = c.erase(it); 即可。

對 map 和 set 來說,它們的 erase 函數返回的 void,而在進行 erase 之後,當前迭代器會失效,無法再用於獲取下一個迭代器。因此需要 erase 之前就獲取指向下一個元素的迭代器。如: 

tmpIt = it;
++it;
c.erase(tmpIt);
利用後綴++操作符的特性(先創建副本,然後再遞增迭代器,然後返回副本)上面的三行代碼可以簡化爲一行:
c.erase(it++);


list 正向遍歷刪除元素示例(vector 用法相同):

    list<int>::iterator it;
    for (it = l.begin(); it != l.end();)
    {
        if (0 == (*it) % 2) {
            it = l.erase(it);
        }
        else {
            ++it;
        }
    }

map 正向遍歷刪除元素示例(set 用法相同)

    map<int, int>::iterator mit;
    for (mit = m.begin(); mit != m.end();)
    {
        if (0 == mit->first % 2) {
            m.erase(mit++);
        }
        else {
            ++mit;
        }
    }

第二種情形,反向遍歷刪除元素

關於正向/反向迭代器的關係,請參考《Effective STL》,在這裏我只說明一點,兩者相差一個元素,從一個反向迭代器獲得對應的正向迭代器需要使用 base() 方法。如下圖所示:ri 是指向元素3的反向迭代器,而 i 是 ri.base() 所得到的正想迭代器。


由於所有的 erase 函數都只接受正向迭代器 iterator,所以在進行反向遍歷刪除元素時,首先需要將 reverse_iterator 轉換爲 iterator,然後再考慮更新迭代器的問題。

先來分析如何將 reverse_iterator 轉換爲 iterator。如上圖所示,我們想要刪除元素3,而 ri.base() 所得到的正向迭代器 i 指向的其實 4 了,因而爲了正確地刪除元素 3,需要將ri往前(反向的)挪一個位置。也就是說,這一步的刪除用法應爲:


c.erase((++rit).base());

或:(想想爲什麼?,但這個用法不具備可移植性,因爲有些 STL 實現不允許修改函數返回的指針)

c.erase(--(rit.base();


然後,我們來分析迭代器更新的問題。
對 list/vector 來說,由於的 erase 能夠返回一個有效的正向迭代器,因而只需要將返回的正向迭代器轉換爲反向迭代器即可。

對 map/set 來說,因爲在進行刪除操作 l.erase((++rit).base()) 時,迭代器已經更新過了,真是一舉兩得啊。從這裏也可以看出,使用這種先遞增後 base() 的轉換刪除法,代碼更清晰。

至此,理論分析完畢,下面我們來看具體的實例。

list 反向遍歷刪除元素示例(vector 用法相同):

    // erase with reverse_iterator
    list<int>::reverse_iterator rit;
    for (rit = l.rbegin(); rit != l.rend();)
    {
        if (0 == (*rit) % 2) {
            rit = list<int>::reverse_iterator(l.erase((++rit).base()));
        }
        else {
            ++rit;
        }
    }

map 反向遍歷刪除元素示例(set 用法相同):

    // erase with reverse_iterator
    map<int, int>::reverse_iterator rit;
    for (rit = m.rbegin(); rit != m.rend();)
    {
        if (0 == rit->first % 2) {
            m.erase((++rit).base());
        }
        else {
            ++rit;
        }
    }

OK,刪除用法相信大家都明白了,但是,但是,引起迭代器失效的操作還有插入操作呀,相信聰明的你一定能夠舉一反三正確更新迭代器~~


發佈了192 篇原創文章 · 獲贊 524 · 訪問量 246萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章