日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

STL源码学习(一)迭代器概念与traits编程技法

發布時間:2024/4/19 编程问答 57 豆豆
生活随笔 收集整理的這篇文章主要介紹了 STL源码学习(一)迭代器概念与traits编程技法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

STL的中心思想在于:將數據容器和算法分開,彼此獨立設計,最后再以一貼膠著劑將它們撮合在一起。這種膠著劑便是迭代器,迭代器設計的目的在于可以方便的獲取(遍歷)容器中的數據同時又不暴露容器的內部實現。

比如說,當使用一個鏈表迭代器時,使用者根本不會知道數據在鏈表中是如何存儲的。這帶來的好處是,所有的STL算法只需要接收迭代器最為參數就夠了,根本不用管是哪個容器的迭代器,只要能夠獲取數據,通過迭代器改變數據就可以了。

不過,迭代器的實現并不是那么簡單,因為在通常使用迭代器時,可能還需要知道迭代器指向的數據類型,比如某些算法中需要定義一個同類型的變量。這就產生了矛盾,因為既不想暴露底層數據,又必須知道數據的類型,后面會看到,通過traits技法,可以很容易獲取迭代器所指類型

類型萃取

traits技法

作為容器專屬的迭代器,容器中保存的數據類型迭代器是知道的(因為需要作為模板參數),所以如果在迭代器中定義內嵌類型,就可以獲取迭代器所指數據的類型,比如定義MyIter迭代器

template <class T> struct MyIter {/* 定義T的別名 */typedef T value_type;T* ptr;MyIter(T* p = 0) : ptr(p) {}T& operator*() const { return *ptr; }//... };templace <class I> /* 使用I的內嵌了類型獲取迭代器所指數據類型 */ typename I::value_type func(I ite) {return *ite; }//... MyIter<int> ite(new int(8)); cout << func(ite); //輸出8

當使用作用域符號表達一個類型時,需要在前面添加typename顯式指出這是一個型別。因為I是一個模板參數,在具現化之前,編譯器對I一無所知,根本不知道I::value_type是什么,所以需要顯式告訴編譯器,這是一個型別

Traits就是對上述內嵌類型獲取迭代器所指數據類型方法的封裝,形象地稱為”萃取”迭代器特性,而value_type正是迭代器特性之一

/* 用于萃取迭代器類型,迭代器指向元素的類型等* 使用時可以直接使用* iterator_traits<I>::value_type獲取迭代器指向元素類型 */ template <class Iterator> struct iterator_traits {typedef typename Iterator::value_type value_type; };

通過這個萃取器,如果Iterator聲明有value_type內嵌類型,那么就可以使用如下方法獲取迭代器Iterator所指類型

templace <class I> /* 函數返回型別,通過iterator_traits從I中萃取出它保存的數據類型 */ typename iterator_traits<I>::value_type func(I iter) {return *iter; }

指針特化版本

多了一層間接性帶來的好處是可以為iterator_traits定義特化版本,由于原生指針也可以算作迭代器的一種,所以像iterator_traits\

/* 針對原生指針的偏特化版本,因為指針沒有內嵌類型 */ template <class T> struct iterator_traits<T*> {typedef T value_type; };/* 針對常量指針的偏特化版本 */ template <class T> struct iterator_traits<const T*> {typedef T value_type; };

有了上面的特化版本,當使用iterator_traits\

內嵌類型

定義

迭代器部分只有iterator_traits萃取功能不容易理解,剩下的部分是關于迭代器型別的,包括

  • value type,迭代器指向的數據值類型
  • different type,迭代器差類型
  • reference type,迭代器指向的數據引用類型
  • pointer type,迭代器指向的數據指針類型
  • iterator_category,迭代器類型

這幾種類型在萃取器iterator_traits中也都有相應的定義,用于萃取出相應型別,同上述的valut_type一樣

/* 用于萃取迭代器類型,迭代器指向元素的類型等 */ /* 使用時可以直接使用* iterator_traits<I>::iteartor_category獲取迭代器I的類型* iterator_traits<I>::value_type獲取迭代器指向元素類型* 依次類推 */ template <class Iterator> struct iterator_traits {typedef typename Iterator::iterator_category iterator_category;typedef typename Iterator::value_type value_type;typedef typename Iterator::difference_type difference_type;typedef typename Iterator::pointer pointer;typedef typename Iterator::reference reference; };
  • value type實際上就是迭代器所指對象的型別,也就是模板參數T。假設使用MyIter it迭代器,那么value type就是int

  • difference type是指迭代器之間的距離類型,如兩個迭代器作差,保存迭代器之間的距離等,其類型就是difference type,這個類型通常是ptrdiff_t類型

  • reference type是指迭代器所指對象的引用類型,通常是T&

  • pointer type是指迭代器所指對象的指針類型,通常是T*

typedef T value_type;typedef ptrdiff_t difference_type;typedef T& reference;typedef T* pointer;

迭代器類型

在STL中,根據迭代器所支持的算術運算,實際上存在多種類型的迭代器,分別是

  • input iterator只讀迭代器,不可以改變迭代器所指對象。只支持++操作
  • ouput iterator只寫迭代器,允許改變迭代器所指對象。只支持++操作
  • forward iterator前向迭代器,可讀寫。只支持++操作
  • bidirectional iterator雙向迭代器,可讀寫。支持++和–操作
  • random access iterator隨機迭代器,可讀寫。支持所有指針算術操作,包括+,-,+=,-=,++,–等

可以看到,不同類型的迭代器可以提供不同的功能,但這同時也帶來了一些問題,以advance函數為例,該函數將給定的迭代器移動n步,然而,不同類型的迭代器支持不同的算術操作,如果都使用++,那么對隨機迭代器而言就過于耗時(因為本可以直接使用+=)。這就是iterator_category迭代器類型的作用,結合模板的參數推導區分不同的迭代器類型,同時采用最有效的方法進行算術操作

模板函數參數推導功能保證根據參數類型選擇最合適的重載函數,所以在STL中,提供了五個類定義分別對應五個迭代器類型

/* 定義迭代器類型,每個迭代器中都會將自己迭代器類型typedef成iterator_categories* 由于C++不支持直接獲取類型操作,所以需要利用模板參數推導實現不同類型的不同操作 */ struct input_iterator_tag {}; struct output_iterator_tag {}; struct forward_iterator_tag : public input_iterator_tag {}; struct bidirectional_iterator_tag : public forward_iterator_tag {}; struct random_access_iterator_tag : public bidirectional_iterator_tag {};

這五個類定義的繼承關系和迭代器之間的繼承關系相同,另外這五個類是空類,因為它的作用僅僅是作為模板參數用于函數重載

在迭代器的定義中會根據自身所支持的功能最強的迭代器類型定義iterator_category,如

typedef random_access_iterator_tag iterator_category;

這樣,當再次使用advance函數時,傳入自己的迭代器類型對應的類,同時根據模板參數推導就可以調用不同的函數

/* 將迭代器i移動n步,n可正可負 */ template <class InputIterator, class Distance> inline void advance(InputIterator& i, Distance n) {/* 不同迭代器支持的操作不同* 只讀,只寫,正向迭代器只支持++操作* 雙向迭代器支持++,--操作* 隨機迭代器支持+=,-=,++,--操作* 為了根據不同迭代器選擇不同的__advance函數,采用模板參數推導的方法實現重載 *//* iterator_category()也是一個模板重載,用于創建一個迭代器i的tag對象* 根據模板參數推導,可以實現__advance重載 */__advance(i, n, iterator_category(i)); }

模板參數中迭代器類型是InputIterator類型的原因是STL將模板參數類型設置為最弱的類型(類似繼承體系中的基類),實際類型會根據i實際的類型判斷(類似于繼承體系中的多態)

iterator_category函數用于獲取迭代器對應的tag對象

/* 返回迭代器類型對象,注意返回的是一個類對象* 這是因為模板參數推導只針對類對象進行推導*/ template <class Iterator> inline typename iterator_traits<Iterator>::iterator_category iterator_category(const Iterator&) {typedef typename iterator_traits<Iterator>::iterator_category category;return category(); }

至此,根據__advance(i, n, iterator_category(i))函數調用的第三個參數類型,模板參數推導可以實現調用不同的函數

/* 針對只讀迭代器的__advance重載 */ template <class InputIterator, class Distance> inline void __advance(InputIterator& i, Distance n, input_iterator_tag) {while (n--) ++i; }/* 針對雙向迭代器的__advance重載 */ template <class BidirectionalIterator, class Distance> inline void __advance(BidirectionalIterator& i, Distance n, bidirectional_iterator_tag) {if (n >= 0)while (n--) ++i;elsewhile (n++) --i; }/* 針對隨機迭代器的__advance重載 */ template <class RandomAccessIterator, class Distance> inline void __advance(RandomAccessIterator& i, Distance n, random_access_iterator_tag) {i += n; }

STL中只提供了三個__advance函數重載,這是因為上述的繼承體系。由于output迭代器,forward迭代器的__advane操作和input迭代器的操作相同,所以完全可以向上調用input迭代器的對應函數(好比調用派生類的某個函數,但是派生類中不存在這個函數,就會向上調用基類的對應函數)

小結

迭代器部分的難點有兩個,一個是通過traits技法實現類型萃取,另一個是通過模板參數推導實現函數重載。不過真正看過源碼后會發現其實并不困難,當然,STL需要內部的迭代器都遵循它的規定,即定義上述的五個型別

總結

以上是生活随笔為你收集整理的STL源码学习(一)迭代器概念与traits编程技法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。