Boolan C++ STL與泛型編程_3

主要內容:

本節深入剖析了各種常用容器和容器適配器的底層支撐,容器主要分為三大類,順序容器、關聯容器、無序容器。其中主要介紹了順序容器中deque的內部實現,以及默認deque作為底層支撐的兩個容器適配器stack和queue。并且對紅黑樹進行了深入探索,以及由它實現的set、multiset、map、multimap。還有對hash table進行了詳細分析,由它實現了unordered容器。

1. deque

  • deque是分段(buffer)連續的,deque內部利用vector存放所有段的首地址。向deque的頭尾插入數據時,都會先查看最后一個段是否還有空閑空間,如果有,則直接插入數據;如果沒有,那么會新分配一個段,并把段的地址存放在vector中,再向其插入數據。

  • deque的iterator是一個class,內部存放4個指針(first,last,cur,node),其中node是指向vector中第一個數據段的指針,利用這個指針可以使cur指針自增自減到另一個段。first指向一個段的頭,last指向一個段的尾(段中最后一個元素的下一個位置)。first和last表示段的邊界,每次自增自減都要判斷是否到達邊界,如果是,那么要借助node指針,將iterator指向下一個段。cur表示段中當前指向的位置。當在一個段內操作時,只有cur會變動,當到達邊界切換到下一個段時,這4個指針都要改變。

  • deque中iterator的大小是16字節(4個指針)。deque中有4個數據成員:兩個iterator分別指向deque的begin和finish,一個指向vector的指針(map)和vector的大小(map_size),所以共40個字節(32位cpu)。

  • deque的模板參數有3個,包括元素類型、分配器、BufSiz(默認是0). __deque_buf_size用于計算buffer size. 如果沒有指定BufSiz,那么會根據元素的大小(sz)設置buffer size,如果一個元素的大小小于512,那么要用512除以元素的大小來計算buffer size. 如果大于等于512,buffer size會設置為1.

  • deque<T>::insert(iterator pos, const value_type &x) 允許指定位置放入元素。內部會先判斷pos是否在頭或尾(pos.cur是否是start.cur或是finish.cur),如果是的話,那么會調用push_front(x)或push_back(x)實現。如果不是,那么就要在中間插入元素調用insert_aux(pos, x)。insert_aux插入數據時會先判斷插入點前后哪一邊的元素少,取少的那一邊全部元素向前移動一個位置,之后空出來的位置放入x。這樣做可以盡可能高效。

  • deque如何模擬連續空間:
    *it是取得iterator的cur的值.
    difference_type operartor-(const self& x) const 得到兩個迭代器之間的距離(元素個數),中間完整緩沖區個數(node-x.node-1)*bufsiz+起始buffer的元素數量(x.last-x.cur)+結尾buffer的元素數量(cur-first)。
    用后++調用前++,用后--調用前--. 前++會先++cur,檢查cur是否到達邊界,如果是,則set_node(node+1),node,first和last要重設。前--會先判斷cur==first,如果是,那么set_node(node-1)切換到前一個緩沖區的最后一個元素的位置。
    deque也重載了+=,-=,+,-運算符,迭代器可以一次移動n個位置。先判斷是否會跨越緩沖區,如果是,那么再計算要跨越幾個緩沖區,再移動node指針來跨越緩沖區,到達最后一個緩沖區,再計算要偏移幾個元素,最后到達指定的位置。operator-=相當于+= -n,所以-=是利用operator+=來實現的。operator[]是利用operator+實現的,operator+是利用operator+=實現的。operater-是利用operator-=實現的,所以歸根結底[],-=,+,-這三個運算符重載底層都是由self & operator+=(difference_type n)實現的。

  • G4.9不允許用戶調整buffer size的大小,但是也是利用__deque_buf_size函數隊友沒有指定bufSiz的情況的策略計算buffer size大小。

  • deque中維護buffer地址的vector每次按照2倍大小進行擴充,擴充后,會將之前的元素拷貝到中段,前后都能留有空閑空間,便于前后擴展。

2. queue和stack

  • 它們內部擁有一個deque. 他們的各個函數都是調用的deque的函數。queue和stack也可以選擇list作為底部支撐,默認是選擇deque. 使用方式eg.stack<string, list<string>> c; queue<string, list<string>> q; 選擇deque作為底部支撐效率更高。
  • queue和stack都不允許遍歷,也不提供iterator,因為不允許中間插入/刪除元素。
  • queue不能用vector作為底部支撐,但stack可以,因為pop操作vector所提供的函數中沒有pop_front函數,編譯器會在調用pop的時候報錯,但不調用pop不會報錯,也就是說當在定義一個用vector作為底部支撐的queue時,編譯器不會檢查deque提供的各個函數是否可用,只有在調用各個函數的時候才會檢查。
  • queue和stack也不允許選擇set和map作為底部支撐。因為set不會提供pop_front,push_back,back等函數接口,所以在調用時會保錯。在定義deque的時候就不能使用map作為底層的支持,因為map存放的是鍵值對,但queue和stack中需要的是一個類型的元素。所以在定義時就會報錯。

3. RB-tree深度探索

  • 紅黑樹是平衡二元搜索樹。有利于查找和插入。遍歷時,++iter得到的元素是按照key排序的。map允許改變data,不允許改變key. rb_tree提供兩種insert操作:insert_unique()和insert_equal().
  • rb_tree的模板參數:key, value(key和data合成value),KeyofValue(從value中取key的方法),Compare(兩個節點key比較大小的方法),Alloc=alloc.
  • rb_tree的數據成員:node_count(節點數量),header(指向節點的指針),key_compare(比較key的準則,是個泛函數,大小是1) G2.9 大小是12(4+4+1=9,對齊后是12).G4.9中是24包含3個指針和一個枚舉類型_Rb_tree_color(1個字節)。
  • G4.9的stl結構設計采用handler and body 分離(base和impl分離)的方式.
  • set和map都是由紅黑樹實現的。

4. set和multiset

  • 對于set來講key就是value,不能改變元素值。set插入數據是調用的rb_tree的insert_unique(), multiset插入數據是調用的rb_tree的insert_equal().
template<class Key, class Compare = less<Key>, class Alloc = alloc> class set{...};
eg. set<int> iset;<==> template<int, int, identity<int>, less<int>, alloc>class rb_tree;
  • set的iterator是rb_tree中的const_interator,防止修改iterator指向的元素。
  • set的所有操作都是調用底層rb_tree的操作。所以set也未嘗不是一個container adapter.
  • vc6不提供identity(),是創建一個泛函數實現的。

5. map和multimap

  • 這里key是const類型,防止用戶更改key.
eg. map<int, string> imap; <==>
template<int, pair<const int, string>, select1st<pair<const int, string>>, less<int>, alloc> class rb_tree; 
  • map的iterator就是rb_tree的iterator.
  • vc6不提供select1st函數。
  • operator[]如果map中沒有這個key,就會自動創建并插入這個pair, 且value是默認值。operator[]的內部實現,iterator _i = lower_bound(_k);如果map中沒有_k, 那么會返回適合放置_k的位置,之后會調用insert插入到map中。

6. hashtable

  • 當元素個數比籃子的個數多的時候,會將hashtable打散,將籃子增加到大約是原有的2倍。gcc中將籃子的數量已經保存在static const unsigned long __stl_prime_list[]數組中,擴展籃子的數量時會查看這個數組。G2.9中的做法:初始籃子的個數是53,之后擴展時,53*2=106,會在106附近找到一個素數(97),作為籃子新的數量。之后所有的元素要重新計算放置位置。

  • class hashtable要指定6個模板參數:value,key, class HashFcn(hash function計算元素對應的編號hash code), class ExtractKey(從元素中取出key), class EqualKey(計算key相等的方法), class Alloc=alloc.

  • hashtable的大小:HashFcn,EqualKey,ExtractKey的對象各占1個字節,vector<node*,Alloc> buckects占12字節(內部有3個指針)。size_type num_elements占4個字節。總共19個字節,對齊后是20個字節。__hastable_node中有一個指向下一個節點的指針和value。

  • hashtable中iterator的設計,考慮到遍歷完一個鏈表后,要能夠回到hashtable, 再遍歷下一個鏈表,所以iterator中除了有一個指向當前節點的指針外node* cur, 還有一個指向hashtable本身的指針hashtable* ht目的就是能回到hashtable再遍歷下一個鏈表。

  • hash-function,hash-code: 標準庫中有hash類的泛化模板,也有對數值類型和char*類型元素計算hash-code特化,元素是數值的話,會默認就將這個數值作為hash-code, 也可以用戶自己寫這樣的特化hash類,設計得到hash-code。注意:標準庫中沒提供hash<std::string>需要自己寫.

  • 計算元素落在哪個籃子的方法:hash(key)%n.

  • eg. G2.9 hashtable<const char, const char, hash<const char *>, identity<const char >, eqstr, alloc>ht(50, hash<const char>(), eqstr()); 第一個參數50是設置籃子的數量,實際會查看__stl_prime_list數組(G4.9版和G2.9有區別,這是G2.9版的情況),找到第一個比50大的籃子數量53進行分配。

  • G4.9的hashtable名稱變了,并且參數由6個變成了10個。

7. unordered容器

c++11將hash_set,hash_multiset,hash_map,hash_multimap這4個名字改為unordered_set, unordered_multiset, unordered_map,unordered_multimap.
c.bucket_size(i)打印第i個籃子中元素的數量。

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

推薦閱讀更多精彩內容