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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

STL源码剖析 迭代器iterator的概念 和 traits编程技法

發布時間:2023/12/13 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 STL源码剖析 迭代器iterator的概念 和 traits编程技法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
  • iterator模式定義如下:提供一種方法,使之能夠依序巡訪某個 聚合物(容器)所含的各個元素,而又無需暴露該聚合物的內部表述方式.
  • STL的中心思想在于:將數據容器(containers)和算法(algorithms)分開,彼此獨立設計,最后再以一帖膠著劑將它們撮合在一起。容器和算法的泛型化,從技術角度來看并不困難,C ++的class templates和 function templates可分別達成目標。如何設計出兩者之間的良好膠著劑,才是大難題。

?迭代器 (iterator ) 是一種 smart pointer

  • 迭代器是一種行為類似指針的對象,而指針的各種行為中最常見也最重要的便是內容提領〈dereference)和成員訪問(member access) , 因此,迭代器最重要的 編程工作就是對 operator* 和 operator-> 進行重載(overloading) 工作。關于 這一點,C++標準程序庫有一個auto_ptr可供我們參考任何一本詳盡的C++ 語法書籍都應該談到auto_ptr 這是一個用來包裝原生指 針 (native pointer)的對象,聲名狼藉的內存漏洞(memory leak)問題可藉此獲得解決. auto_ptr用法如下,和原生指針一模一樣:

  • 函數第一行的意思是,以算式new動態配置一個初值為"jjhou” 的 string 對象,并將所得結果(一個原生指針)作為aUtoj>tr<Strin g > 對象的初值。注意, auto_ptr角括號內放的是“原生指針所指對象”的型別,而不是原生指針的型別.?

迭代器相 應 型 別 ( associated types )

  • ?我們以func ()為對外接口,卻把實際操作全部置于func_impl()之中.由于 func_impl ()是一個function template,—旦被調用,編譯器會自動進行template參數推導。于是導出型別T,順利解決了問題。
  • 迭代器相應型別(associatedtypes)不 只 是 “迭代器所指對象的型別”?種而 已。根據經驗,最常用的相應型別有五種,然而并非任何情況下任何一種都可利用上述的template參數推導機制來取得.我們需要更全面的解法。

Traits編程技法— STL源代碼門鑰

  • 迭代器所指對象的型別,稱為該迭代器的value iy p e .上述的參數型別推導 技巧雖然可用于value ty p e ,卻非全面可用:萬一 value typ e 必須用于函數的傳 回值,就束手無策了,畢竟函數的*"template參數推導機制”推而導之的只是參數,無法推導函數的回返值型別。
  • 我們需要其它方法。聲明內嵌型別似乎是個好主意,像這樣:

  • 注 意 ,func ( ) 的回返型別必須加上關鍵詞typename , 因 為 T 是一個 template參數,在它被編譯器具現化之前,編譯器對T 一無所悉,換句話說,編 譯器此時并不知道MyIter<T>: : value_type代表的是一個型別或是一個member function或是一個data m e m b e r . 關 鍵 詞 typename的用意在于告訴編譯器這是一個型別,如此才能順利通過編譯。

  • ?大致的意義是:如 果 class template擁有一個以上的 template參數,我們可以針對其中某個(或數個,但非全部)template參數進行特化工作。換句話說,我們可以在泛化設計中提供一個特化版本(也就是將泛化版本中的某些template參數賦予明確的指定)。

  • ?多了一層間接性,先前使用一個T,現在使用了兩個T
  • 但這除了多一層間接性,又帶來什么好處呢?好處是traits可以擁有特化版本。現在,我們令 iterator_traites 擁有一個 partial specializations 如下

  • ?于是,原生指針i n t * 雖然不是一種class type,亦可通過traits取 其 value type。這就解決了先前的問題。
  • 但是請注意,針 對 “指向常數對象的指針(pointeEo-snst) ”,下面這個 式子得到什么結果
  • iterator_traits<const int*>::value_type
  • 獲得的是const i n t 而非i n t . 這是我們期望的嗎?我們希望利用這種機制來聲 明一個暫時變量,使其型別與迭代器的value type相同,而現在,聲明一個無法 賦 值 (因 c o n s t 之故)的暫時變量,沒什么用!因此,如果迭代器是個pointer-to-const,我們應該設法令其value t y p e 為一個non-const型別。沒問題,只要另外設計一個特化版本,就能解決這個問題:

  • ?現在,不論面對的是迭代器My I t e r , 或是原生指針i n t * 或 const int*,都可以通過traits取出正確的(我們所期望的)value type。?
  • 特性萃取機”角色,萃取各個迭代器的特性。 這里所謂的迭代器特性,指的是迭代器的相應型別(associated types)。當然,若 要 這 個 “特性萃取機” traits能夠有效運作,每一個迭代器必須遵循約定,自行以內嵌型別定義(nested typedef)的方式定義出相應型別(associated types) 。這是一個約定,誰不遵守這個約定,誰就不能兼容于S T L 這個大家庭。

  • ?根據經驗,最常用到的迭代器相應型別有五種:value 1ype, difference iype, pointer, reference, iterator catagoly。如果你希望你所開發的容器能與STL水乳交融,一定要為你的容器的迭代器定義這五種相應型別。 “特性萃取機” traits會很 ?實地將原汁原味榨取出來:

?3.4.2 迭 代 器 相應型別之二:difference type

  • difference type用來表示兩個迭代器之間的距離,因此它也可以用來表示一個 容器的最大容量,因為對于連續空間的容器而言,頭尾之間的距離就是其最大容量.
  • 如果一個泛型算法提供計數功能,例如STL的 count(), 其傳回值就必須使用迭代器的 difference type:

  • ?針對相應型別difference type, traits的如下兩個(針對原生指針而寫的)特 化版本,以 C++內建的ptrdiff_t (定義于<cstddef>頭文件)作為原生指針的 difference type:
  • ptrdiff_t是C/C++標準庫中定義的一個與機器相關的數據類型。ptrdiff_t類型變量通常用來保存兩個指針減法操作的結果

?3.4.3 迭代器相應型別之三:reference type

  • 從 “迭代器所指之物的內容是否允許改變”的角度觀之,迭代器分為兩種:不 允 許 改 變 “所指對象之內容”者,稱為constant iterators,例 如 const int*pic; 允許改變"所指對象之內容”者,稱 為 mutable iterators, 例 如 int* pio當我們對 一 個 mutable iterators進行提領操作時,獲得的不應該是一個右值(rvalue), 應該是一個左值(lvalue), 因為右值不允許賦值操作(assignm ent), 左值才允許:

  • ?在 C ++中,函數如果要傳回左值,都是以by reference的方式進行,所以當p 是 個 mutable iterators時,如果其value type 是 T,那 么 * p 的型別不應該是T, 應該是T&
  • 將此道理擴充,如果p 是 一 個 constant iterators,其 value type 是 t ,那 么 * p 的型別不應該是const T , 而應該是const T&。這里所討論的* p 的型 別,即所謂的reference type

迭代器相應型別之四:pointer type

  • pointers和 references在 C + + 中有非常密切的關聯。如 果 “傳回一個左值, 令它代表P 所指之物”是可能的,那 么 “傳回一個左值,令它代表P 所指之物的地址”也一定可以。也就是說,我們能夠傳回一個pointer,指向迭代器所指之物。

  • item& 便是 Listlier 的 reference type ,而 item * 便是其 pointer type

?迭代器 相 應 型 別 之 五 :iterator_category

  • 最后一個(第五個)迭代器的相應型別會引發較大規模的寫代碼工程。在那之前,我必須先討論迭代器的分類
  • 根據移動特性與施行操作,迭代器被分為五類:

  • ?盡量針對圖3? 2中的某種迭代器提供一個明確定義,并針對更強化的某種迭代器提供另一種定義,這樣才能在不同情況下提供最大效率。在研究STL的過程中,每一分每一秒我們都要謹記在心,效率是個重要課題。假設有個算法可接受Forward Iterator,你 以 Random Access Iterator喂給它, 它當然也會接受,因為一個Random Access Iterator必然是一個Forward Iterator(見圖3-2) 。但是可用并不代表最佳!

以 advanced。 為例

  • 拿 advance () 來 說 (這是許多算法內部常用的一個函數),該函數有兩個 參數,迭代器P 和數值n;函數內部將p 累進n 次 (前進n 距離)。下面有三份定義,一份針對 Input Iterator, 一份針對 Bidirectional Iterator,另一份針對 Random Access Iteratoro倒是沒有針對Foiv/ardlterator而設計的版本,因為那和 針 對 Inputiterator而設計的版本完全~致。
  • 設計考慮如下:如 果 traits有能力萃取出迭代器的種類,我們便可利用這個 “迭代器類型”相應型別作為advanced 0 的第三參數。這個相應型別一定必須是一個class type,不能只是數值號碼類的東西,因為編譯器需仰賴它(一個型別)來進行重載決議(overloaded resolution) o 下面定義五個classes,代表五種迭代器類型:

  • ?這 些 classes只作為標記用,所以不需要任何成員。至于為什么運用繼承機制, 稍后再解釋。現在重新設計— advance。 (由于只在內部使用,所以函數名稱加 上特定的前導符),并加上第三參數,使它們形成重載:

?

  • ?注意上述語法,每個— advanced 的最后一個參數都只聲明型別,并未指定 參數名稱,因為它純粹只是用來激活重載機制,函數之中根本不使用該參數。如果硬要加上參數名稱也可以,畫蛇添足罷了。
  • 行進至此,還需要一個對外開放的上層控制接口,調用上述各個重載的_advance()。這一上層接口只需兩個參數,當它準備將工作轉給上述的 _advance ()時,才自行加上第三參數:迭代器類型。因此,這個上層函數必須有 能力從它所獲得的迭代器中推導出其類型—— 這份工作自然是交給traits機制:

  • ?任何一個迭代器,其類型永遠應該落在“該迭代器所隸屬之各種類型中,最強化的那個”。例如,int* 既是 Random Access Iterator,又是 Bidirectional Iterator, 同時也是Forward Iterator,而且也是Input Iterator,那么,其類型應該歸屬為 random_access_iterator_tag?
  • 按 說 advanced()既然可以接受各種類型的迭代器,就不應將其型別參數命 名為Inputiterator。這其實是STL算法的一個命名規則:以算法所能接受之最 低階迭代器類型,來為其迭代器型別參數命名
  • 消 除 "單純傳遞調用的函數
  • 以 class來定義迭代器的各種分類標簽,不僅可以促成重載機制的成功運作 (使編譯器得以正確執行重載決議,overloaded resolution), 另一個好處是,通過繼承,我們 可 以 不 必 再 寫 “單純只做傳遞調用”的函數(例如前述的 advance() Forwarditerator版 ) 。為什么能夠如此?考慮下面這個小例子,從其輸出結果可以 看出端倪:

?以 d is ta n c e ()為 例

  • 關 于 “迭代器類型標簽”的應用,以下再舉一例。distance ()也是常用的一個迭代器操作函數,用來計算兩個迭代器之間的距離。針對不同的迭代器類型,它可以有不同的計算方式,帶來不同的效率。整個設計模式和前述的advance ()如出一轍:

  • ?distance使用 category動態適配 InputIterator和randomAccessiterator,分別調用與之匹配的__distance函數,但是這個 distance使用的時候 <> 需要指定最小的迭代器類型,來為迭代器進行命名

  • ?注意,distanced可接受任何類型的迭代器;其 template型別參數之所以命 名 為 Inputiterator,是為了遵循STL算法的命名規則:以算法所能接受之最初 級類型來為其迭代器型別參數命名.此外也請注意,由于迭代器類型之間存在著繼承關系, “傳遞調用(forwarding) ”的行為模式因此自然存在一 一點我已在 前一節討論過。換句話說,當客端調用distanced并使用Output Iterators或Forward Iterators 或 BidirectionaI Iterators 時,統統都會傳遞調用 Input Iterator 版
    的那個_ distance ( ) 函數。

std::iterator 的保證

  • 了符合規范,任何迭代器都應該提供五個內嵌相應型別,以利于traits萃取,否則便是自別于整個STL架構,可能無法與其它STL組件順利搭配。然而寫代碼難免掛一漏萬,誰也不能保證不會有粗心大意的時候。如果能夠將事情簡化,就好多了。STL提供了一個iterators class如下,如果每個新設計的迭代器都 繼承自它,就可保證符合STL所需之規范:

  • ?設計適當的相應型別(associated types) , 是迭代器的責任。設計適當的迭代 器,則是容器的責任.唯容器本身,才知道該設計出怎樣的迭代器來遍歷自己,并執行迭代器該有的各種行為(前進、后退、取值、取用成員…) 。至于算法,完全可以獨立于容器和迭代器之外自行發展,只要設計時以迭代器為對外接口就行。

ite ra to r源代碼完整重列

  • SGI STL<stl_iterator.h>頭文件內與本章相關的程序代碼。該頭文件還有其它內容,是關于 iostream iterators, inserter iterators 以及 reverse iterators 的實現,將于第8 章討論。

?我并不是很懂為啥要再包裝一層

?SGI STL 的 私 房 菜 :__type_traits

  • traits編程技法很棒,適度彌補了 C++語言本身的不足。STL只對迭代器加 以規范,制定出itera to r_ tra its這樣的東西。SG I把這種技法進一步擴大到迭 代器以外的世界,于是有了所謂的_ type_tra-itso 雙底線前綴詞意指這是SGI STL內部所用的東西,不在STL標準規范之內?
  • iterator_ traits負責萃取迭代器的特性,—type_ traits則負責萃取型別(type)的特性
  • 此處我們所關注的型別特性是指:這個型別是否具備non-trivial defaltctor ? 是否具備 non-trivial copy ctor ? 是否具備 non-trivial assignment operator?是否具備non-trivial dtor?如果答案是否定的,我們在對這個型別進行構造、析構、拷貝、賦值等操作時,就可以采用最有效率的措施(例如根本不調用身居高位,不謀實事的那些constructor, destructor), 而采用內存直接處理操作如 malloc. memcpy等等,獲得最高效率。這對于大規模而操作頻繁的容器, 有著顯著的效率提升4。

  • ?我們希望上述式子響應我們“真”或 “假”(以便我們決定采取什么策略),但其結果不應該只是個bool值,應該是個有著真/假性質的“對象”,因為我們 希望利用其響應結果來進行參數推導,而編譯器只有面對Class object形式的參數, 才會做參數推導。為此,上述式子應該傳回這樣的東西:

  • ?為了達成上述五個式子,— type_traits內必須定義一些typedefs,其值不是 _ true_type 就是 _ false_type下面是 SGI 的做法:

POD

?

例子

?

?

?

?

?

?

?

?

總結

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

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