1. 程式人生 > >在遍歷中使用 iterator/reverse_iterator 進行 Erase 的用法

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

原文地址:http://blog.csdn.net/kesalin/article/details/24265303


眾所周知,在使用迭代器遍歷 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 之前就獲取指向下一個元素的迭代器。如: 

[cpp]  view plain  copy
  1. tmpIt = it;  
  2. ++it;  
  3. c.erase(tmpIt);  
利用字尾++操作符的特性(先建立副本,然後再遞增迭代器,然後返回副本)上面的三行程式碼可以簡化為一行:
[cpp]  view plain  copy
  1. c.erase(it++);  


list 正向遍歷刪除元素示例(vector 用法

相同):

[cpp]  view plain  copy
  1. list<int>::iterator it;  
  2. for (it = l.begin(); it != l.end();)  
  3. {  
  4.     if (0 == (*it) % 2) {  
  5.         it = l.erase(it);  
  6.     }  
  7.     else {  
  8.         ++it;  
  9.     }  
  10. }  

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

[cpp]  view plain  copy
  1. map<intint>::iterator mit;  
  2. for (mit = m.begin(); mit != m.end();)  
  3. {  
  4.     if (0 == mit->first % 2) {  
  5.         m.erase(mit++);  
  6.     }  
  7.     else {  
  8.         ++mit;  
  9.     }  
  10. }  

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

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


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

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

[cpp]  view plain  copy
  1. c.erase((++rit).base());  

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

[cpp]  view plain  copy
  1. c.erase(--(rit.base();  


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

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

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

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

[cpp]  view plain  copy
  1. // erase with reverse_iterator  
  2. list<int>::reverse_iterator rit;  
  3. for (rit = l.rbegin(); rit != l.rend();)  
  4. {  
  5.     if (0 == (*rit) % 2) {  
  6.         rit = list<int>::reverse_iterator(l.erase((++rit).base()));  
  7.     }  
  8.     else {  
  9.         ++rit;  
  10.     }  
  11. }  

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

[cpp]  view plain  copy
  1. // erase with reverse_iterator  
  2. map<intint>::reverse_iterator rit;  
  3. for (rit = m.rbegin(); rit != m.rend();)  
  4. {  
  5.     if (0 == rit->first % 2) {  
  6.         m.erase((++rit).base());  
  7.     }  
  8.     else {  
  9.         ++rit;  
  10.     }  
  11. }  

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