【C++後臺開發面試】STL六大組件(一)

1.六大組件及其關係
Container(容器) 各種基本數據結構
Adapter(適配器) 可改變containers、Iterators或Function object接口的一種組件
Algorithm(算法) 各種基本算法如sort、search…等
Iterator(迭代器) 連接containers和algorithms
Function object(函數對象)
Allocator(分配器)
(一)容器-Container
容器類是容納、包含一組元素或元素集合的對象 
七種基本容器:向量(vector)、雙端隊列(deque)、列表(list)、集合(set)、多重集合(multiset)、映射(map)和多重映射(multimap) 


序列式容器
序列式容器Sequence 
containers,其中每個元素均有固定位置——取決於插入時機和地點,和元素值無關。(vector、deque、list)
list:雙向鏈表:

基於雙向環狀鏈表實現;尾部有空白節點(爲滿足左閉右開);內部有一個 last 迭代器指向尾端空白節點(其 next 爲 begin 迭代器)。
插入和刪除快,但是隨機訪問比較慢,時間複雜度爲O(1);
需要頻繁進行插入或刪除操作且不需要過多地在序列內部進行長距離跳轉,應該選擇list
List 不能使用算法 sort(只接受隨機存取迭代器),值接受雙向迭代器,不支持隨機存取迭代器,它有自己內置的 sort。
vector:

動態數組,基於數組的實現,
從後面插入和刪除元素,push_back,pop_back,隨機訪問快 ,插入和刪除慢,因爲會造成內存塊的拷貝,時間複雜度爲O(n)
迭代器在增加數據,內存擴容時必定失效,因爲內存地址都變了,刪除數據時看編譯器情況,vs會失效。
維護三個迭代器:start,finish,end_of_storage;
內存分配: 
vector動態數組只會增加內存,不會刪除空間,當空間不夠時會自動申請另一片更大的空間,然後把原有數據拷貝過去,並刪除原來的空間的數據,但是存儲空間不會釋放,要等到vector調用析構函數的時候纔會釋放空間。
擴容原則是: 
VS2015 下配對源碼 每次擴容50%,原來空間大小9,擴容之後9+9/2=13;
Ubuntu 下源碼是按每次增長兩倍算;
若原本空間爲0,第一次配置擴容爲1,否則按1.5或2倍來來算
強制釋放內存:.clear()函數只會清空數據,並不會釋放內存,一般採用swap函數釋放空間。通過創建臨時拷貝對象,調用swap之後來釋放原對象內存空間。vector< int>(v).swap(v);注意:這裏的swap是泛型算法裏的swap函數,不是容器裏的。
注意:並不是所有的STL容器的clear成員函數的行爲都和vector一樣。事實上,其他容器的clear成員函數都會釋放其內存。比如另一個和vector類似的順序容器deque。
deque(雙向隊列):

與vector類似,也是基於數組,但是支持開始端插入元素:push_front;
與vector相比元素存取和迭代器動作稍慢,但是內存分配方面優於vector
c++標準建議:vector是那種應該在默認情況下使用的序列。如果大多數插入和刪除操作發生在序列的頭部或尾部時,應該選用deque。
使用中控器 map,存的指針,指向實際存儲塊
迭代器失效: 
a. 在deque容器首部或者尾部插入元素不會使得任何迭代器失效; 
b. 在其首部或尾部刪除元素則只會使指向被刪除元素的迭代器失效; 
c. 在deque容器的任何其他位置的插入和刪除操作將使指向該容器元素的所有迭代器失效。
關聯式容器
關聯式容器Associative

containers,元素位置取決於特定的排序準則以及元素值,和插入次序無關。(set、multiset、map、multimap)
set:一對一, 內部結構採用紅黑樹的平衡二叉樹。自動排序,默認升序,不允許重複值
multiset:類同set,允許重複值
map (key,value): 一對多, 內部結構採用紅黑樹的平衡二叉樹。自動排序,默認升序,不允許重複值
multimap:類同map,允許重複值
紅黑樹:五大特性

每個節點或者是黑色,或者是紅色。
根節點是黑色。
每個葉子節點是黑色。 [注意:這裏葉子節點,是指爲空的葉子節點!]
如果一個節點是紅色的,則它的子節點必須是黑色的。
從一個節點到該節點的子孫節點的所有路徑上包含相同數目的黑節點。 


紅黑樹插入結點

因爲紅黑樹上面的第4個特點,因此當向紅黑樹中插入新的節點時,應該將新節點標註爲紅色。向紅黑樹中插入節點看的是插入節點的父節點和叔父節點。

紅黑樹旋轉

右旋 
插入D結點,此時的樹不滿足紅黑樹性質,需要旋轉,這裏需要對A進行右旋轉

以 A-B軸右旋,對A右旋,A成爲B的右孩子,B的右孩子成爲A的左孩子

同理,若需要左旋,則是以A-B軸右旋,對A左旋,A成爲B的左孩子,B的左孩子成爲A的右孩子
(二)迭代器-Iterators
迭代器Iterators,用來在一個對象羣集(collection of 
objects)的元素上進行遍歷。這個對象羣集或許是個容器,或許是容器的一部分。迭代器的主要好處是,爲所有容器提供了一組很小的公共接口。迭代器以++進行累進,以*進行提領,因而它類似於指針,我們可以把它視爲一種smart 
pointer

比如++操作可以遍歷至羣集內的下一個元素。至於如何做到,取決於容器內部的數據組織形式。
每種容器都提供了自己的迭代器,而這些迭代器能夠了解容器內部的數據結構。
迭代器是一種智能指針,智能指針定義爲存儲指向動態分配對象指針的類,迭代器封裝了指針的同時,還對指針的一些基本操作如*、->、++、==、!=、=進行了重載,使其具有了遍歷複雜數據結構的能力,其遍歷機制取決於所遍歷的數據結構。如operator++運算符,對於數組就是普通的++下一個元素,對於鏈表則是先去next,再取元素。
迭代器分類:在STL中原生指針也是一種迭代器,除此之外還有五種迭代器

Input Iterator:此迭代器不允許修改所指的對象,即是隻讀的。支持==、!=、++、*、->等操作。
Output Iterator:允許算法在這種迭代器所形成的區間上進行只寫操作。支持++、*等操作。
Forward Iterator:允許算法在這種迭代器所形成的區間上進行讀寫操作,但只能單向移動,每次只能移動一步。支持Input Iterator和Output Iterator的所有操作。
Bidirectional Iterator: 允許算法在這種迭代器所形成的區間上進行讀寫操作,可雙向移動,每次只能移動一步。支持Forward Iterator的所有操作,並另外支持–操作。
Random Access Iterator:包含指針的所有操作,可進行隨機訪問,隨意移動指定的步數。支持前面四種Iterator的所有操作,並另外支持it + n、it - n、it += n、 it -= n、it1 - it2和it[n]等操作。 
 
只有順序容器和關聯容器支持迭代器遍歷,各容器支持的迭代器的類別如下:

vector 隨機訪問
deque 隨機訪問
list 雙向
set 雙向
multiset 雙向
map 雙向
multimap 雙向
stack 不支持
queue 不支持
priority_queue 不支持
三、算法-Algorithm
算法Algorithms,用來處理羣集內的元素。它們可以出於不同的目的而搜尋、排序、修改、使用那些元素。通過迭代器的協助,我們可以只需編寫一次算法,就可以將它應用於任意容器,這是因爲所有的容器迭代器都提供一致的接口。

四、適配器-Adapter
適配器是一種類,爲已有的類提供新的接口,目的是簡化、約束、使之安全、隱藏或者改變被修改類提供的服務集合
三種類型的適配器:

容器適配器:用來擴展7種基本容器,它們和順序容器相結合構成棧、隊列和優先隊列容器,stack,queue, 
priority_queue可以基於vector和deque,採用最大堆來實現,因爲需要隨機存取迭代器,只有這兩個;
迭代器適配器(反向迭代器、插入迭代器、IO流迭代器)
函數適配器(函數對象適配器、成員函數適配器、普通函數適配器)
五、函數對象(仿函數)-function object
一個行爲類似函數的對象,它可以沒有參數,也可以帶有若干參數。
任何重載了調用運算符operator()的類的對象都滿足函數對象的特徵
函數對象可以把它稱之爲smart function。
STL中也定義了一些標準的函數對象,如果以功能劃分,可以分爲算術運算、關係運算、邏輯運算三大類。爲了調用這些標準函數對象,需要包含頭文件< functional>。
六、分配器-allocator(*)
參考:https://blog.csdn.net/md521/article/details/42046043 
負責空間配置與管理。從實現的角度來看,配置器是一個實現了動態空間配置、空間管理、空間釋放的class template。 
隱藏在這些容器後的內存管理工作是通過STL提供的一個默認的allocator實現的。當然,用戶也可以定製自己的allocator,只要實現allocator模板所定義的接口方法即可,然後通過將自定義的allocator作爲模板參數傳遞給STL容器,創建一個使用自定義allocator的STL容器對象,如: 
stl::vector< int, UserDefinedAllocator> array; 
大多數情況下,STL默認的allocator就已經足夠了。這個allocator是一個由兩級分配器構成的內存管理器,當申請的內存大小大於128byte時,就啓動第一級分配器通過malloc/free直接向系統的堆空間分配,如果申請的內存大小小於128byte時,就啓動第二級分配器,從一個預先分配好的內存池中取一塊內存交付給用戶,這個內存池由16個不同大小(8的倍數,8~128byte)的空閒列表組成,allocator會根據申請內存的大小(將這個大小round up成8的倍數)從對應的空閒塊列表取表頭塊給用戶。

這種做法有兩個優點: 
(1)小對象的快速分配。小對象是從內存池分配的,這個內存池是系統調用一次malloc分配一塊足夠大的區域給程序備用,當內存池耗盡時再向系統申請一塊新的區域,整個過程類似於批發和零售,起先是由allocator向總經商批發一定量的貨物,然後零售給用戶,與每次都總經商要一個貨物再零售給用戶的過程相比,顯然是快捷了。當然,這裏的一個問題時,內存池會帶來一些內存的浪費,比如當只需分配一個小對象時,爲了這個小對象可能要申請一大塊的內存池,但這個浪費還是值得的,況且這種情況在實際應用中也並不多見。 
(2)避免了內存碎片的生成。程序中的小對象的分配極易造成內存碎片,給操作系統的內存管理帶來了很大壓力,系統中碎片的增多不但會影響內存分配的速度,而且會極大地降低內存的利用率。以內存池組織小對象的內存,從系統的角度看,只是一大塊內存池,看不到小對象內存的分配和釋放。

出處:http://blog.csdn.net/jnu_simba/article/details/9410459
--------------------- 
作者:shiluoxuan 
來源:CSDN 
原文:https://blog.csdn.net/shiluoxuan/article/details/81356635 
版權聲明:本文爲博主原創文章,轉載請附上博文鏈接!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章