STL源碼學習----迭代器及其適配器


  “迭代器是連接容器和算法的紐帶,它們為數據提供了一種抽象的觀點,使寫算法的人不必關心多種多樣的數據結構的具體細節。”-----<C++程序設計語言>

  SGI STL 3.3中的stl_iterator_base.h 和stl_iterator.h兩個頭文件中定義了跟迭代器相關的一些類。本文首先會介紹迭代器的基本概念,然后分析與迭代器相關的五種類型及traits(萃取)方法,最后簡要介紹迭代器的幾種適配器。

 

1,迭代器概述

  迭代器是指向序列元素的指針的一種抽象。通過使用迭代器,我們可以訪問序列中的某個元素、改變序列中的某個元素的值、使迭代器向前或向后行走等等。

  依據有效執行操作的情況,迭代器可以分為五類:輸入迭代器、輸出迭代器、前向迭代器、雙向迭代器和隨機存取迭代器。STL中用五個類來代表這五種迭代器類別:

 

  其中

 

  · Input Iterator 所指的對象不允許外界改變

 

  · Output Iterator 支持對該迭代器所指對象的寫操作

 

  · Forward Iterator 不僅支持Input Iterator和Output Iterator的操作,還能在序列中前向移動指向下一個元素

 

  · Bidirectional Iterator 可以雙向移動,既可指向序列中的下一個元素,也可以指向序列中的前一個元素

 

  · Random Iterator 既可以雙向移動,又可以跨越多個元素存取對象

1 struct input_iterator_tag {}; 2 struct output_iterator_tag {}; 3 struct forward_iterator_tag : public input_iterator_tag {}; 4 struct bidirectional_iterator_tag : public forward_iterator_tag {}; 5 struct random_access_iterator_tag : public bidirectional_iterator_tag {};

 

從上面的代碼中可以看出,五種類型的迭代器有如下的層次結構:

  每個容器都會根據自身情況定義自己的迭代器類型。

 

2, 與迭代器操作相關的類型及其萃取機制

  使用迭代器是為了獲取被指向的對象和被指向的序列的信息。只要給出描述某個序列的迭代器,用戶就可以通過迭代器間接去操作被指向的對象,並且可以確定序列中元素的個數。為了表述這種操作,STL必須能提供與迭代器有關的各種類型。

   迭代器在元素操作的時候需要用到以下五種類型:

  value_type: 代表迭代器所指對象的類型。

  difference_type:代表兩個迭代器之間的距離

  reference_type:代表迭代器所指對象的引用類型。簡言之,它是operator*()的返回類型

  pointer_type:代表迭代器所致對象的指針類型。簡言之,它是operator->()的返回類型

  iterator_category:代表1中提出的五種迭代器的類型標識

  每種容器都會提供自身迭代器的這五種類型,以下是從list的實現代碼stl_list.h中摘錄的迭代器類型定義:

1 template <class _Tp, class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) >
2 class list : protected _List_base<_Tp, _Alloc> { 3  ...//此處勝率若干代碼
4 public: 5   typedef _List_iterator<_Tp,_Tp&,_Tp*> iterator; 6  ... 7 };

  迭代器類_List_iterator中定義了我們所需要的五種類型

 1 template<class _Tp, class _Ref, class _Ptr>
 2 struct _List_iterator : public _List_iterator_base {  3   ... //此處省略若干代碼
 4  typedef _Tp value_type;  5  typedef _Ptr pointer;  6  typedef _Ref reference;  7    //注:下面兩行在基類_List_iterator_base中
 8  typedef ptrdiff_t difference_type;  9  typedef bidirectional_iterator_tag iterator_category; 10  ... 11 };

  為了方便算法的使用,iterator_traits會將每種容器定義的五種類型萃取出來:

1 template <class _Iterator>
2 struct iterator_traits { 3  typedef typename _Iterator::iterator_category iterator_category; 4  typedef typename _Iterator::value_type value_type; 5  typedef typename _Iterator::difference_type difference_type; 6  typedef typename _Iterator::pointer pointer; 7  typedef typename _Iterator::reference reference; 8 };

  另外,STL還針對<T*>和<const T*>提供了特化版本:

1 template <class _Tp>
2 struct iterator_traits<_Tp*> { 3   ...//省略
4 }; 5 
6 template <class _Tp>
7 struct iterator_traits<const _Tp*> { 8   ...//省略
9 };

   iterator_traits扮演了類似於下圖的一個角色:

   iterator_traits為屏蔽了下層各容器類型的不同,為上層的應用(主要是一些算法,如advance()等)提供了統一的用戶界面。

  下圖是advance函數的調用關系圖:

  

 

3,迭代器的適配器

  STL提供了許多基於迭代器的適配器,如back_insert_iterator, front_insert_iterator, inser_iterator, reverse_iterator, istream_iterator, ostream_iterator, istreambuf_iterator, ostreambuf_iterator等。

  這些適配器大致可以分為三類:插入迭代器、反向迭代器和IO迭代器。下面一一介紹這三類迭代器,重點會放在反向迭代器上。

  3.1 插入迭代器

    插入迭代器用於將值插入到容器中。插入迭代器是一個模板類,模板參數為容器,迭代器只需要在重載操作符函數operator=()中調用容器的插入操作(對應的push_back, push_front或insert)即可。   

insert_iterator實現

 

  3.2 反向迭代器

    顧名思義,反向迭代器會提供與普通迭代器相反方向的遍歷功能。反向迭代器其實一個正向迭代器的適配器,它的實現都是通過調用正向迭代器的操作,為了與迭代器的概念保持一致(begin指向迭代器的第一個元素,end指向迭代器的最后一個元素的下一個位置),又與正向迭代器有一點點不同。

    reverse_iterator的實現中有一個名為current的Iterator,它是模板參數,即正向迭代器。正向迭代器指向的范圍是序列中的第一個元素到最后一個元素的下一個位置,為了保持迭代器概念的統一,反向迭代器的rbegin應該是序列的最后一個元素,rend應該是第一個元素前面的元素。

    所以,current總是指向reverse_iterator所指元素之后的一個元素。這也意味這*返回的是值*(current-1),++通過對current的--實現。下面貼上reverse_iterator的代碼。  

reverse_iterator實現
 1 template <class _Iterator>
 2 class reverse_iterator  3 {  4 protected:  5  _Iterator current;  6 public:  7   typedef typename iterator_traits<_Iterator>::iterator_category  8  iterator_category;  9   typedef typename iterator_traits<_Iterator>::value_type 10  value_type; 11   typedef typename iterator_traits<_Iterator>::difference_type 12  difference_type; 13   typedef typename iterator_traits<_Iterator>::pointer 14  pointer; 15   typedef typename iterator_traits<_Iterator>::reference 16  reference; 17 
18  typedef _Iterator iterator_type; 19   typedef reverse_iterator<_Iterator> _Self; 20 
21 public: 22  reverse_iterator() {} 23   explicit reverse_iterator(iterator_type __x) : current(__x) {} 24 
25   reverse_iterator(const _Self& __x) : current(__x.current) {} 26 #ifdef __STL_MEMBER_TEMPLATES 27   template <class _Iter>
28   reverse_iterator(const reverse_iterator<_Iter>& __x) 29     : current(__x.base()) {} 30 #endif /* __STL_MEMBER_TEMPLATES */
31     
32   iterator_type base() const { return current; } 33   reference operator*() const { 34     _Iterator __tmp = current; 35     return *--__tmp; 36  } 37 #ifndef __SGI_STL_NO_ARROW_OPERATOR 38   pointer operator->() const { return &(operator*()); } 39 #endif /* __SGI_STL_NO_ARROW_OPERATOR */
40 
41   _Self& operator++() { 42     --current; 43     return *this; 44  } 45   _Self operator++(int) { 46     _Self __tmp = *this; 47     --current; 48     return __tmp; 49  } 50   _Self& operator--() { 51     ++current; 52     return *this; 53  } 54   _Self operator--(int) { 55     _Self __tmp = *this; 56     ++current; 57     return __tmp; 58  } 59 
60   _Self operator+(difference_type __n) const { 61     return _Self(current - __n); 62  } 63   _Self& operator+=(difference_type __n) { 64     current -= __n; 65     return *this; 66  } 67   _Self operator-(difference_type __n) const { 68     return _Self(current + __n); 69  } 70   _Self& operator-=(difference_type __n) { 71     current += __n; 72     return *this; 73  } 74   reference operator[](difference_type __n) const { return *(*this + __n); } 75 }; 

 

  3.3 IO迭代器

  標准庫提供4個迭代器類型,以使流能夠融入容器和算法的框架中:

  ostream_iterator: 用於向ostream中寫入

  istream_iterator: 用於向istream中讀出

  ostreambuf_iterator: 用於向流緩沖區寫入

  istreambuf_iterator: 用於從流緩沖區讀出

  輸入輸出迭代器的思想是將輸入輸出流當作序列,ostream_iterator和istream_iterator相當於指向序列的迭代器,用戶可以通過這個迭代器對輸入輸出流做操作。但是,ostream_iterator的迭代器類型是input_iterator,只支持寫操作(*p=X)和迭代操作(++);istream_iterator的迭代器類型是output_iterator,它可以支持讀(=*p), 訪問(->),迭代(++),比較(==, !=)操作

  下面的代碼是ostream_iterator的實現代碼:

ostream_iterator的實現
 1 template <class _Tp,  2           class _CharT = char, class _Traits = char_traits<_CharT> >
 3 class ostream_iterator {  4 public:  5  typedef _CharT char_type;  6  typedef _Traits traits_type;  7   typedef basic_ostream<_CharT, _Traits> ostream_type;  8 
 9  typedef output_iterator_tag iterator_category; 10   typedef void value_type; 11   typedef void difference_type; 12   typedef void pointer; 13   typedef void reference; 14 
15   ostream_iterator(ostream_type& __s) : _M_stream(&__s), _M_string(0) {} 16   ostream_iterator(ostream_type& __s, const _CharT* __c) 17     : _M_stream(&__s), _M_string(__c) {} 18   ostream_iterator<_Tp>& operator=(const _Tp& __value) { 19     *_M_stream << __value; 20     if (_M_string) *_M_stream << _M_string; 21     return *this; 22  } 23   ostream_iterator<_Tp>& operator*() { return *this; } 24   ostream_iterator<_Tp>& operator++() { return *this; } 25   ostream_iterator<_Tp>& operator++(int) { return *this; } 26 private: 27   ostream_type* _M_stream; 28   const _CharT* _M_string; 29 };

 

  用戶可以像下面一樣使用ostream_iterator:

1     ostream_iterator<string> os(cout); 2     *++os = "cobb"; 3     *os = "liu";

  上面的這段程序會產生輸出cobbliu。在使用ostream_iterator的時候需要像使用正常迭代器一樣將迭代器作迭代操作(++)。

  istream_iterator的實現跟ostream_iterator的實現大相徑庭,值得注意的是istream_iterator中有一個標志標識輸入序列是否結束,它的默認構造函數會將istream_iterator指向輸入序列的結束位置(M_ok=false),表示輸入序列結束。當對輸入迭代器作迭代操作(++)的時候,會判斷M_ok的值,如果該值為false,則終止讀過程。

  

  istreambuf_iterator和ostreambuf_iterator可以使用戶跨過iostream,直接跟流緩沖區打交道。

 

4,總結

  迭代器將算法和數據結構有效地分離並粘合起來。實際上,STL中的很多容器都設計有自己專屬的迭代器,因為只有它自己才能知道自身數據結構的布局及其遍歷方法,只需要按照標准聲明上面提到的五種迭代器要用到的數據類型即可。迭代器更多的是運用到算法中,有了泛型和迭代器的支持,算法可以達到很高的內聚性。

 

5,參考資料

  1)《STL源碼剖析》 侯捷著

  2) SGI STL 3.3源代碼

  3)《C++程序設計語言》 Bjarne Stroustrip先生。

 

 

 


免責聲明!

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



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