GeekBand C++ STL 第一周

模版介紹

STL中主要由容器迭代器算法三個部件構成

容器用來管理對象的集合,每一種容器都有自己的優缺點,儲存的方式等都有所不同,使用時需根據程序的需求考慮不同容器的效率來選擇

迭代器為所有容器提供了一組公共接口,并且,每一種容器都提供自己的迭代器

STL中把數據和算法分開,賦予了STL極大的彈性

下圖演示了三個部件之間的交互關系

可以看出,迭代器是容器和算法之間的接口,總體說來,STL使容器與算法分離,使其二者不需要相互依賴,而迭代器又將算法和不同的容器stick在一起,從而使需要的算法能夠運用到不同的容器上(例如可以對多種容器使用find函數,中介便是迭代器)。

模版是C++的一種特性,允許函數或類通過泛型的形式表現和運行。模版的類型有 類模版,函數模版, 成員模版等。

模版的實例化是從模版構建出一個真正函數或類的過程。模版被編譯了兩次。沒有被實例化之前,檢查模版代碼本身是否有語法錯誤,在實例化期間,檢查對模版代碼的調用是否合法。

函數模版是參數化的一族函數。通過模版函數,可以定義一系列函數。這些函數都是基于同一套代碼,但是可以作用在不同型別的參數上

C++類模版,與函數模版類似,類也可以通過參數泛化,從而可以構建出一族不同型別的類實例。類模版實參可以是某一型別或常量。


類模版的特化,允許對一個類模版的某些模版參數類型做特化。特化的作用在于:對于某種特殊的型別,可能可以做些特別的優化或提供不同的實現;避免在實例化類的時候引起一些可能產生的詭異行為。類模版同樣可以特化或者偏特化。 如果有不止一個偏特化銅等程度地能匹配某個應用,那么該調用具有二義性,會產生編譯錯誤。

C++類模版參數可以有默認值。

Traits(特性)

Traits在面向對象程序設計中,是一個不可實例化(uninstantiable)的方法與類型的集合,為一個對象或算法提供了策略(policy)或實現自身界面的細節功能。

Traits作為模板類,既聲明了統一的界面(包括類型、枚舉、函數方法等),又可以通過模板特化,針對不同數據類型或其他模板參數,為類、函數或者通用算法在因為使用的數據類型不同而導致處理邏輯不同時,提供了區分不同類型的具體細節,從而把這部分用Traits實現的功能與其它共同的功能區分開來。例如,容器的元素的不同數據類型,或者iostream是使用char還是wchar_t。

C++標準模板庫中大量使用了traits。將因為模板形參(包括類型形參、非類型形參)不同而導致的不同抽取到新的模板(即traits)中去;然后通過traits的模板特化來實現針對具體情況的優化實現。一個traits包括了enum、typedef、模板偏特化(template partial specialization)。其中,enum定義了各種類的標識的統一表示;typedef定義了各個類的各自不同的類型定義,這對于使用模板元編程(template meta-programming)的靈活性非常重要;模板偏特化用于實現各個類的不同功能。

Trabit Class 如何設計

確認若干希望將來可以取得的類型信息,比如對于Iterator而言,我們希望可以取得其category

為信息選擇一個名稱,例如 iterator_category

提供一個template和一組特化版本,內含你希望支持的類型相關信息

總結:

建立一組重載函數或者函數模板(例如doAdvance),彼此之間的差異只在于各自的trabit參數。令各個函數實現碼與其接受之trabits信息相應和

建立一個控制函數或者函數模板(例如advance),它調用上述的"勞工函數",并傳遞traits class提供的信息

迭代器 Iterator

迭代器是一種 Pointer-like class。是指針的泛化。迭代器本身是一個對象。在STL中迭代器是容器與算法之間的接口。在STL中,算法和容器是分離的,而迭代器就是它們之間的粘合劑。如 find 算法,接收一對迭代器作為參數,分別指向容器的開始和結束。

Iterator也是一種很長的設計模式的手法,用來遍歷一個Resource,這樣可以屏蔽很多內部操作,直接利用了C++的operator++/operator--/operator+/operator-重載。

當然C++的iterator也是有好幾個分類的。

1.輸入迭代器(input iterator)

2.輸出迭代器(output iterator)

3.前向迭代器(forward iterator)

4.雙向迭代器(bidirectional iterator)

5.隨機存取迭代器(random access iterator)

輸入迭代器(input iterator)

input iterator就像其名字所說的,工作的就像輸入流一樣.我們必須能

取出其所指向的值

訪問下一個元素

判斷是否到達了最后一個元素

可以復制

因此其支持的操作符有? *p,++p,p++,p!=q,p == q這五個.凡是支持這五個操作的類都可以稱作是輸入迭代器.當然指針是符合的.

輸出迭代器(output iterator)

output iterator工作方式類似輸出流,我們能對其指向的序列進行寫操作,其與input iterator不相同的就是*p所返回的值允許修改,而不一定要讀取,而input只允許讀取,不允許修改.

支持的操作和上頭一樣,支持的操作符也是 *p,++p,p++,p!=q,p == q.

前向迭代器(forward iterator)

前向迭代器就像是輸入和輸出迭代器的結合體,其*p既可以訪問元素,也可以修改元素.因此支持的操作也是相同的.

雙向迭代器(bidirectional iterator)

雙向迭代器在前向迭代器上更近一步,其要求該種迭代器支持operator--,因此其支持的操作有*p,++p,p++,p!=q,p == q,--p,p--

隨機存取迭代器(random access iterator)

即如其名字所顯示的一樣,其在雙向迭代器的功能上,允許隨機訪問序列的任意值.顯然,指針就是這樣的一個迭代器.

對于隨機存取迭代器來說, 其要求高了很多:

可以判斷是否到結尾(a==b or a != b)

可以雙向遞增或遞減(--a or ++a)

可以比較大小(a < b or a > b or a>=b ...etc)

支持算術運算(a + n)

支持隨機訪問(a[n])

支持復合運算(a+= n)

容器 Containter

我們所用的常用的數據結構不外乎 array, list, tree, stack, queue, hash table, set, map 等。這些數據結構分為序列式(sequence)和關聯式(associative)兩種。

Sequence Container : array, vector, list, deque

Associative Containter : set, map, multimap, unordered map

對于Sequence Container,我們一般認為這個是一個動態的數組,只是實現上各有差異,可以為鏈表,這個差異就是索引時候的速度,數組是O(1), 鏈表是O(n)。且從空間上來說動態數組在capacity相同的時候空間占用會比鏈表要少一些。但是鏈表在指定位置插入的成本會比數組少很多,是O(1)。

題目說明:

給定一個 vector:v1 = [0, 0, 30, 20, 0, 0, 0, 0, 10, 0],希望通過not_equal_to 算法找到到不為零的元素,并復制到另一個 vector: v2。

在查了not_equal_to用法后,寫下的第一個版本:

#include <vector>

#include <functional>

#include <iostream>

using namespace std;

int main(intargc,char** argv)

{

vector v1 {0,0,30,20,0,0,0,0,10,0};

vector v2;

constintFIND_NUM =0;

for(inti : v1)?

{if(not_equal_to()(i, FIND_NUM)){? ? ? ? ? ? v2.push_back(i);? ? ? ? }? ? }for(inti : v2)cout<< i << endl;return0;}

也就是把i!=FIND_NUM改成了not_equal_to()(i, FIND_NUM)這樣的判斷條件,雖然符合題目要求,但不符合題目初衷。

進一步的,我使用了find_if算法進行不為0元素的查找,由于每次都只能找到第一個不為0,所以迭代器在每次找到一個元素后需要移動到下一個元素的位置。

也是符合題目要求,但不符合題目意圖。我想這題是希望通過算法組合,直接一行代碼搞定需求。所以像find_if這樣只能尋找到第一個符合條件元素的也不適用于這題。繼而我找到了copy_if。

其中的問題顯而易見,由于無法使用push_back,必須先讓v2擁有v1一樣的大小,才能保證不溢出,并且需要一個迭代器記錄copy后v2中符合條件的最后一個元素位置,進行resize。

盡管vector在內部處理的時候會向系統申請一塊空間,本質上來說v2初始化為v1相同的大小不一定就浪費空間了,但是這樣的做法還是過于麻煩。

上面的做法只差了一步,如何每次插入到v2后面一個位置,通過back_inserter可以解決。

由于copy_if是C++11才有的,之前可以用remove_copy_if替代,且篩選條件也需要做下修改,

用課上講的erase+remove_if也可以,但需要事先拷貝v1所有元素到v2,或者先修改了v1的數據,留下的拷貝到v2.

改變原數組的方法并不可取。另外也可以使用partition_copy將v1分成0和非0元素兩組,

以上所有代碼當中的

for(inti:v2)

cout << i << endl;

也可以改成

copy(v2.begin(), v2.end(), ostream_iterator(cout,"\n"));

在有算法可以使用的時候,盡量使用算法,比如將v1有效的數據拷貝入v2的時候,不考慮用push_back/insert,因為后者相對來說效率低。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,002評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,400評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,136評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,714評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,452評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,818評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,812評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,997評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,552評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,292評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,510評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,035評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,721評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,121評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,429評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,235評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,480評論 2 379

推薦閱讀更多精彩內容