C++迭代器之'反向迭代器'


反向迭代器(Reverse Iterator)是普通迭代器的適配器,通過重新定義自增和自減操作,以達到按反序遍歷元素的目的。如果在標准算法庫中用反向迭代器來代替普通的迭代器,那么運行結果與正常情況下相反。除此之外,其用法與普通迭代器完全一樣,我們不作詳細討論。

反向迭代器reverse_iterator是一種反向遍歷容器的迭代器,也就是從最后一個元素到第一個元素遍歷容器。反向迭代器的自增(或自減)的含義反過來了:對於反向迭代器,++運算符將訪問前一個元素,–運算符將訪問下一個元素。 

 

反向迭代器與迭代器的轉換 

    reverse_iterator與iterator都繼承自_Iterator_base,它們是可以相互轉換的。 

  • 調用reverse_iterator的base()方法可以獲取"對應的"iterator。 
  • 可以用iterator構造一個"對應的"reverse_iterator。 

   下面的兩個例子展示了它們之間的轉換: 

list<int> test_list; 
for (size_t i = 1; i < 8; i++) 

    test_list.push_back( i*2 ); 

list<int>::reverse_iterator rit = find(test_list.rbegin(), test_list.rend(), 8); 
list<int>::iterator it(rit.base()); 
cout << *rit << endl; 
cout << *it << endl;

 

   上面的代碼是先查找到一個指向8的reverse_iterator,並reverse_iterator的base()方法得到一個iterator。但是從輸出上看,iterator指向的元素的值並不是8,而是10。 

 

list<int> test_list; 
for (size_t i = 1; i < 8; i++) 

    test_list.push_back( i*2 ); 

list<int>::iterator it = find(test_list.begin(), test_list.end(), 8); 
list<int>::reverse_iterator rit(it); 
cout << *it << endl; 
cout << *rit << endl; 

   上面的代碼是先查找一個指向8的iterator,然后用該iterator構造一個reverse_iterator。但是從輸出上看,reverse_iterator指向的元素並不是8,而是6。 

 

       接下來主要討論的是反向迭代器的一個很特殊、也很容易出錯的性質,即它的“邏輯位置”與“物理位置”。先通過看一個例子開始:

[cpp]  view plain  copy
 
  1. vector<int> vec;  
  2. for(vector<int>::size_type i=1; i<10; ++i)  
  3. {  
  4.     vec.push_back(i);  
  5. }  
  6.   
  7. vector<int>::iterator itr = vec.begin()+4;  
  8. cout<<*itr<<endl;  
  9. vector<int>::reverse_iterator r_itr(itr);  
  10. cout<<*r_itr<<endl;  

這個例子中,vec中存放從1到9的9個連續數字,並初始化一個普通迭代器指向數字5,打印輸出結果顯而易見,為5。然后再初始化一個反向迭代器,該迭代器指向與普通迭代器一樣的物理位置,然后打印輸出,這時結果為多少呢?

       如果你不了解反向迭代器這個特殊的性質的話,很容易誤認為結果一樣是5。但實際情況不是這樣,而是4,即前一個位置處的元素!具體原因,即涉及到反向迭代器的“物理位置”與“邏輯位置”兩個概念。

       我們都知道,一個容器的范圍用普通迭代器表示為一個“半開半閉”的區間。頭部為begin,指向容器第一個元素的位置。末尾為end,指向最后一個元素的下一個位置,每個容器都提供了這樣一個位置,盡管該位置不可引用,但卻是個合法的地址。相反,第一個元素位置的前一個位置容器卻沒有任何保證,比如對於vector和string來說,就是非法的位置。這里我們說“合法”與“非法”,簡單來講,可以這樣認為,一個合法的位置對於迭代器來說是可以達到的,像最后一個元素的下一個位置end()。而對於首元素的前一個位置,迭代器是無法指向它的,begin()-1這個表達式會導致異常。因此,反向迭代器與普通迭代器在物理位置上保持了一一對應,即rbegin()對應普通迭代器的end()位置,rend()對應其begin()位置。

       但是,為了讓反向迭代器與普通迭代器在概念上保持一致性,即begin()(反向迭代器對應為rbegin())對應第一個元素(對於反向迭代器來說,最后一個元素即第一個元素),end()(反向迭代器對應為rend())對應最后一個元素的下一個位置,於是標准庫的設計者們想出這樣一個方法,即反向迭代器的邏輯位置等於其物理位置的前一個位置。換句話說,物理位置對應迭代器在內存中的實際位置,邏輯位置對應迭代器對應容器中元素的位置。這樣,對於rbegin()來說,它物理位置是容器最后一個元素的下一個位置,邏輯位置即容器最后一個元素的位置(對反向迭代器來說就是第一個元素元素的位置),同理rend()物理位置為容器第一個元素位置,邏輯位置即第一個位置的前一個位置(依然不可解引用)。這樣,反向迭代器與普通迭代器便有了一致的概念,即“半開半閉”區間。更為直觀的演示如下圖:

 

       (該圖片來自:《The C++ Standard Library, A Tutorial And Reference》)

        這樣,開頭那個例子就很容易解釋了,反向迭代器被初始化為與前一個普通迭代器一樣的物理位置(對應元素5),其邏輯位置即前一個位置,因些通過解引用得到元素4.

 

 

反向迭代器的操作 

   了解了反向迭代器與迭代器的關系,再來對容器進行操作就很簡單了。下面是常用操作的例子,例子中用的test_list的內容如上圖所示: 

1. 遍歷容器 

for(list<int>::reverse_iterator rit = test_list.rbegin(); rit != test_list.rend(); ++rit) 

    cout<< *rit << " "; 

2. 插入元素 

    假設要在rit指向的位置插入一個新元素150,那么150應該在8和10之間。對於rit而言,150應該插入到rit的前一個位置。對於it (it = rit.base()),150應該插入it的前一個位置。 

所以,要實現在一個reverse_iterator rit指出的位置上插入新元素,在rit.base()指向的位置插入就行了。 

    操作代碼如下: 

list<int>::reverse_iterator rit = find(test_list.rbegin(), test_list.rend(), 8); 
test_list.insert(rit.base(), 150); 

3. 刪除元素 

    假設要刪除rit指向的元素,就不直能刪除rit.base()指向的元素了,rit指向的是8,而rit.base()指向的是10。實際上我們要刪除的是rit.base()的前一個元素。 

所以,要實現在一個reverse_iterator rit指出的位置上刪除元素,那么刪除rit.base()的前一個元素就行了。 

操作代碼如下: 

for(list<int>::reverse_iterator rit = test_list.rbegin(); rit != test_list.rend();) 

    if (8 == *rit) 
    { 
        list<int>::iterator it = –rit.base() ;  // 用(++rit).base()也可; 
        list<int>::iterator it_after_del = test_list.erase(it); 
        rit = list<int>::reverse_iterator(it_after_del); 
    } 
    else 
    { 
        ++rit; 
    } 

    調用erase()刪除元素后,返回值是指向被刪除的元素的下一個元素的iterator,所以還需要把返回的iterator變成reverse_iterator賦值給rit。 

當然,上面的代碼寫成這樣也是一個意思: 

for(list<int>::reverse_iterator rit = test_list.rbegin(); rit != test_list.rend();) 

    if (8 == *rit) 
    { 
        rit = list<int>::reverse_iterator(test_list.erase(–rit.base())); 
    } 
    else 
    { 
        ++rit; 
    } 
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM