STL源碼剖析--迭代器(轉)

1、爲何須要traits編程技術

 

       前面說了不少關於traits的光榮事蹟,可是卻一直沒有介紹traits到底是個什麼東西,到底是用來幹什麼的?traits在英文解釋中就是特性,下面將會引入traits技術的做用,一步一步地揭開其神祕的面紗。算法

 

1.1 內嵌類型聲明

 

 

1.1.1 以迭代器所指對象的類型聲明局部變量

 

       下面是一個以迭代器爲模板形參的函數模板:編程

  1. template<typename Iterator>  
  2. void func(Iterator iter)  
  3. {  
  4.     //函數體  
  5. }  
 
template<typename Iterator>
void func(Iterator iter)
{
    //函數體
}

       假如如今算法中須要聲明一個變量,而變量的類型是迭代器所指對象的類型,應該怎麼處理呢?dom

  1. template<typename Iterator>  
  2. void func(Iterator iter)  
  3. {  
  4.     *Iterator var;//這樣定義變量能夠嗎?  
  5. }  
 
template<typename Iterator>
void func(Iterator iter)
{
    *Iterator var;//這樣定義變量能夠嗎?
}

       上面的代碼是不能夠經過編譯的,雖然C++支持sizeof(),可是並不支持typeof(),就算是用到RTTI性質中的typeid(),獲取到的也僅僅是類型的名字,所以不能直接用來聲明變量。此時能夠利用函數模板的參數類型推導機制解決問題,例如:函數

  1. template<typename Iterator, typename T>  
  2. void func_impl(Iterator iter, T t)  
  3. {  
  4.     T temp;//這裏就解決了問題  
  5.     //這裏作本來func()的工做  
  6. }  
  7.   
  8. template<typename Iterator>  
  9. void func(Iterator iter)  
  10. {  
  11.     func_impl(iter, *iter);//func的工做所有都移到func_impl裏面了  
  12. }  
  13.   
  14. int main(int argc, const char *argv[])  
  15. {  
  16.     int i;  
  17.     func(&i);  
  18. }  
 
template<typename Iterator, typename T>
void func_impl(Iterator iter, T t)
{
    T temp;//這裏就解決了問題
    //這裏作本來func()的工做
}

template<typename Iterator>
void func(Iterator iter)
{
    func_impl(iter, *iter);//func的工做所有都移到func_impl裏面了
}

int main(int argc, const char *argv[])
{
    int i;
    func(&i);
}

       函數func做爲對外接口,實際的操做卻由函數func_impl執行,經過函數func_impl的參數類型推導,獲取到Iterator指向對象的類型T,從而解決了問題。學習

 

1.1.2 以迭代器所指對象的類型聲明返回類型

 

       如今經過函數模板的參數類型推導解決了函數體內聲明變量的問題,但問題又來了,若是須要返回類型是迭代器所指對象的類型又能夠怎樣作呢?spa

  1. template<typename Iterator>  
  2. (*Iterator) func(Iterator iter)  
  3. {  
  4.     //這樣定義返回類型能夠嗎?  
  5. }  
 
template<typename Iterator>
(*Iterator) func(Iterator iter)
{
    //這樣定義返回類型能夠嗎?
}

       在這種狀況下,模板的參數類型推導機制也無能爲力了,由於它只能推導參數,並不能推導函數的返回類型。STL解決這種問題的辦法就是內嵌類型聲明,即在迭代器內部添加一種「特性」,經過這種「特性」,算法能夠很容易地獲知迭代器所指對象的類型,請看下面的代碼:.net

  1. template<typename T>  
  2. class Iterator  
  3. {  
  4. public:  
  5.     typedef T value_type;//內嵌類型聲明  
  6.     Iterator(T *p = 0) : m_ptr(p) {}  
  7.     T& operator*() const { return *m_ptr;}  
  8.     //...  
  9.   
  10. private:  
  11.     T *m_ptr;  
  12. };  
  13.   
  14. template<typename Iterator>  
  15. typename Iterator::value_type  //以迭代器所指對象的類型做爲返回類型,長度有點嚇人!!!  
  16. func(Iterator iter)  
  17. {  
  18.     return *iter;  
  19. }  
  20.   
  21. int main(int argc, const char *argv[])  
  22. {  
  23.     Iterator<int> iter(new int(10));  
  24.     cout<<func(iter)<<endl;  //輸出:10  
  25. }  
 
template<typename T>
class Iterator
{
public:
    typedef T value_type;//內嵌類型聲明
    Iterator(T *p = 0) : m_ptr(p) {}
    T& operator*() const { return *m_ptr;}
    //...

private:
    T *m_ptr;
};

template<typename Iterator>
typename Iterator::value_type  //以迭代器所指對象的類型做爲返回類型,長度有點嚇人!!!
func(Iterator iter)
{
    return *iter;
}

int main(int argc, const char *argv[])
{
    Iterator<int> iter(new int(10));
    cout<<func(iter)<<endl;  //輸出:10
}

       函數func()的返回類型前面必須加上關鍵詞typename,緣由在本人以前寫的「C++模板學習」中也解釋過,由於T是一個template參數,編譯器在編譯實例化func以前,對T一無所知,就是說,編譯器並不知道Iterator<T>::value_type是一個類型,或者是一個靜態成員函數,仍是一個靜態數據成員,關鍵詞typename的做用在於告訴編譯器這是一個類型,這樣才能順利經過編譯。設計

 

1.2 原生指針也是一種迭代器

 

       以前在介紹迭代器的分類之時說過,原生指針也是一種迭代器,此時問題就來了,原生指針並非一種類類型,它是沒法定義內嵌類型的。所以,上面的內嵌類型實現還不能徹底解決問題,那可不能夠針對原生指針作特殊化的處理呢?答案是確定的,利用模板偏特化就能夠作到了。指針

       《泛型思惟》一書對模板偏特化的定義是:code

        針對template參數更進一步的條件限制所設計出來的一個特化版本。

  1. //這個泛型版本容許T爲任何類型  
  2. template<typename T>  
  3. class C  
  4. {   
  5.     //...  
  6. };    
 
//這個泛型版本容許T爲任何類型
template<typename T>
class C
{ 
    //...
};  

       咱們很容易接受上面的類模板有一個形式以下的偏特化版本:

  1. template<typename T>  
  2. class C<T*>   
  3. {  
  4.     //...   
  5. };  
 
template<typename T>
class C<T*> 
{
    //... 
};

       這個特化版本僅適用於T爲原生指針的狀況,」T爲原生指針」就是「T爲任何類型」的一個更進一步的條件限制。那如何利用模板偏特化解決原生指針不能內嵌類型的問題呢?下面介紹的iterator_traits就是關鍵了。

 

2、迭代器萃取機--iterator_traits

 

 

2.1 原生指針並非一種類類型

 

       STL裏面使用iterator_traits這個結構來專門「萃取」迭代器的特性,前面代碼中提到的value_type就是迭代器的特性之一:

  1. template<typename Iterator>  
  2. struct iterator_traits  
  3. {  
  4.     typedef typename Iterator::value_type value_type;  
  5. };  
 
template<typename Iterator>
struct iterator_traits
{
    typedef typename Iterator::value_type value_type;
};

       若是Iterator有定義value_type,那麼經過iterator_traits做用以後,獲得的value_type就是Iterator::value_type,比較以前寫的版本和經iterator_traits做用後的版本:

  1. template<typename Iterator>  
  2. typename Iterator::value_type  //這行是返回類型  
  3. func(Iterator iter)  
  4. {  
  5.     return *iter;  
  6. }  
  7.   
  8. //經過iterator_traits做用後的版本  
  9. template<typename Iterator>  
  10. typename iterator_traits<Iterator>::value_type  //這行是返回類型  
  11. func(Iterator iter)  
  12. {   
  13.     return *iter;  
  14. }  
 
template<typename Iterator>
typename Iterator::value_type  //這行是返回類型
func(Iterator iter)
{
    return *iter;
}

//經過iterator_traits做用後的版本
template<typename Iterator>
typename iterator_traits<Iterator>::value_type  //這行是返回類型
func(Iterator iter)
{ 
    return *iter;
}

       從長度上看,好像須要敲的代碼更多了,爲何要這麼麻煩加上一層間接層呢?因爲原生指針也是一種迭代器,並且不是一種類類型,所以原生指針並不能定義內嵌類型。這裏經過實現iterator_traits的一個偏特化版本就能夠解決這個問題了,具體的實現以下:

  1. //iterator_traits的偏特化版本,針對迭代器是個原生指針的狀況  
  2. template<typename T>  
  3. struct iterator_traits<T*>  
  4. {  
  5.     typedef T value_type;  
  6. };  
 
//iterator_traits的偏特化版本,針對迭代器是個原生指針的狀況
template<typename T>
struct iterator_traits<T*>
{
    typedef T value_type;
};

       你們在進行函數重載的時候,應該都曾遇到過如下的狀況:

  1. //函數版本一  
  2. void func(int *ptr)  
  3. {   
  4.     //...  
  5. }  
  6.   
  7. //函數版本二  
  8. void func(const int *ptr)  
  9. {   
  10.     //...  
  11. }  
 
//函數版本一
void func(int *ptr)
{ 
    //...
}

//函數版本二
void func(const int *ptr)
{ 
    //...
}

       以上兩個函數雖然函數、形參個數和位置都同樣,但它們不是同一個函數,而是函數重載的一種狀況,也就是說函數形參的const和非const版本是不同的,在函數版本一里面,能夠修改指針ptr指向的數據,可是在函數版本二里面卻不能夠,由於傳入的指針ptr是一個const指針。由此能夠聯想到,當將一個const指針做爲模板形參傳給前面聲明的偏特化版本的iterator_traits會有發生什麼狀況呢?

  1. iterator_traits<const int*>::value_type  //得到的value_type是const int,並非int  
 
iterator_traits<const int*>::value_type  //得到的value_type是const int,並非int

       當咱們想用iterator_traits萃取出value_type並聲明一個臨時變量時,卻發現聲明的變量是const類型,並不能進行賦值,這違背了咱們的用意。咱們須要一種方法區別const和非const才能避免這種誤會的發生,答案很簡單,只要另外再設計一個iterator_traits偏特化版本就能夠了:

  1. template<typename T>  
  2. struct iterator_traits<const T*>  
  3. {  
  4.     typedef T value_type;  
  5. }  
 
template<typename T>
struct iterator_traits<const T*>
{
    typedef T value_type;
}

       如今,不管是自定義的迭代器,仍是原生指針int*或者是const int*,均可以經過iterator_traits獲取到正確的value_type。

 

2.2 iterator_traits中定義的類型

 

       STL根據經驗,定義了迭代器最經常使用到的五種類型:value_type、difference_type、pointer、reference、iterator_category,任何開發者若是想將本身開發的容器與STL結合在一塊兒,就必定要爲本身開發的容器的迭代器定義這五種類型,這樣均可以經過統一接口iterator_traits萃取出相應的類型,下面列出STL中iterator_traits的完整定義:

  1. tempalte<typename I>  
  2. struct iterator_traits  
  3. {  
  4.     typedef typename I::iterator_category iterator_category;  
  5.     typedef typename I::value_type value_type;  
  6.     typedef typeanme I:difference_type difference_type;  
  7.     typedef typename I::pointer pointer;  
  8.     typedef typename I::reference reference;  
  9. };  
 
tempalte<typename I>
struct iterator_traits
{
    typedef typename I::iterator_category iterator_category;
    typedef typename I::value_type value_type;
    typedef typeanme I:difference_type difference_type;
    typedef typename I::pointer pointer;
    typedef typename I::reference reference;
};

       下面會分別介紹一下這五種類型:

(1) 迭代器類型之一:value_type
       value_type就是指迭代器所指對象的類型,例如,原生指針也是一種迭代器,對於原生指針int*,int即爲指針所指對象的類型,也就是所謂的value_type。

(2) 迭代器類型之二:difference_type
       difference_type用來表示兩個迭代器之間的距離,例如:

  1. int array[5] = {1, 2, 3, 4, 5};  
  2. int *ptr1 = array + 1;//指向2  
  3. int *ptr2 = array + 3;//指向4  
  4. ptrdiff_t distance = ptr2 - ptr1;//結果即爲difference_type  
 
int array[5] = {1, 2, 3, 4, 5};
int *ptr1 = array + 1;//指向2
int *ptr2 = array + 3;//指向4
ptrdiff_t distance = ptr2 - ptr1;//結果即爲difference_type

       上面代碼中,指針ptr2與ptr1相減的結果的類型就是difference_type,對於原生指針,STL以C++內建的ptrdiff_t做爲原生指針的difference_type。

(3) 迭代器類型之三:reference_type

       reference_type是指迭代器所指對象的類型的引用,reference_type通常用在迭代器的*運算符重載上,若是value_type是T,那麼對應的reference_type就是T&;若是value_type是const T,那麼對應的reference_type就是const T&。

(4) 迭代器類型之四:pointer

       pointer就是指迭代器所指的對象,也就是相應的指針,對於指針來講,最經常使用的功能就是operator*和operator->兩個運算符。所以,迭代器須要對這兩個運算符進行相應的重載工做:

  1. T& operator*() const { return *ptr; } // T& is reference type  
  2. T* operator->() const { return ptr; } // T* is pointer type  
 
T& operator*() const { return *ptr; } // T& is reference type
T* operator->() const { return ptr; } // T* is pointer type

(5) 迭代器類型之五:iterator_category

       iterator_category的做用是標識迭代器的移動特性和能夠對迭代器執行的操做,從iterator_category上,可將迭代器分爲Input Iterator、Output Iterator、Forward Iterator、Bidirectional Iterator、Random Access Iterator五類,具體爲何要這樣分類,簡單來講,就是爲了儘量地提升效率,這也是STL的宗旨之一。具體的狀況已經在本人的「《STL源碼剖析》學習之迭代器」中詳細介紹過,這裏就不在多說了。

 

2.3 iterator_traits完整定義

 

       爲了保證iterator_traits能夠正常工做,STL提供了一個iterator類,全部自定義的迭代器都必須繼承自它,這樣才能保證這些自定義的迭代器能夠順利地獄其它STL組件進行協做,iterator類具體定義以下:

  1. template<typename Category,  
  2.          typename T,  
  3.          typename Distance = ptrdiff_t,  
  4.          typename Pointer = T*,  
  5.          typename Reference = T&>  
  6. struct iterator  
  7. {  
  8.     typedef Category iterator_category;  
  9.     typedef T value_type;  
  10.     typedef Distance difference_type;  
  11.     typedef Pointer pointer;  
  12.     typedef Reference reference;  
  13. };  
 
template<typename Category,
         typename T,
         typename Distance = ptrdiff_t,
         typename Pointer = T*,
         typename Reference = T&>
struct iterator
{
    typedef Category iterator_category;
    typedef T value_type;
    typedef Distance difference_type;
    typedef Pointer pointer;
    typedef Reference reference;
};

       類iterator不包含任何成員變量,只有類型的定義,所以不會增長額外的負擔。因爲後面三個類型都有默認值,在繼承它的時候,只須要提供前兩個參數就能夠了,如:

  1. template <typename T>  
  2. class ListIter : public std::iterator<std::forward_iterator_tag, T>  
  3. {  
  4.     //...  
  5. }  
相關文章
相關標籤/搜索