STL源碼剖析—序列容器,stl源碼剖析
STL源碼剖析—序列容器
對於STL中的容器,存在一定的內含關係,例如,heap內含一個vector,priority-queue內含一個hep,stack和queue都含有一個deque,set/map/multiset/multimap都內含一個RB-tree,hash_x都內含一個hashtable。
對於序列容器來說,vector和list的插入都是在指向迭代器之前進行插入。同時需要注意的就是,對於vector來說,調用erase()函數也就是清除了幾個連續空間上的元素,調用其解構函式,並沒用釋放原來元素佔用的記憶體,即使clear函數也沒有釋放記憶體,而只是清除了原來vector內的所有元素。等待vector析構的時候才會將記憶體釋放,但是這一點和list還是有區別的,對於List來說,erase也就是刪除一個節點,調用clear是刪除了List中的所有節點也就讓List記憶體得到了釋放。這是一點不同,last的地方。
同時對於List來說,有一個特殊的地方就是具有transfer函數,這個函數可以遷移,將一段區間中的節點遷移到某一個節點的前面。同時transfer所接收的[first,last)區間也可以是同一個list.但是transfer並非是公開介面,list公開提供的是所謂的接合操作splice。
同時list容器內部還提供了reverse()sort() merge()函數,其實這些函數在演算法庫中也有提供,但是在Effective STL中也提到了,如果容器內部自訂了函數,那麼最好使用容器內部的成員函數。
Deque是一種雙向開口的連續線性空間,可以在頭尾分別做元素的插入和刪除操作。Deque允許在常數時間內對起頭端進行元素的插入或移除操作。Deque沒有容量的概念,因為他是動態地以分段連續空間組合而成,隨時可以增加一段新的空間並連結起來。像vector那樣因為空白間不足而重新設定一塊更大空間,然後複製元素,再釋放舊空間這樣的事情在deque中是不會發生的。Deque也提供random access iterator,但是他的迭代器不是普通指標,對deque進行排序操作,為了提高效率,可將deque先完整複製到一個vector身上,將vector排序後,在複製回deque.
要明白vector擴充容量的代價是很大的,需要申請更大的空間,倒騰資料,銷毀未經處理資料。Deque由一段一段的定量連續空間構成,一旦有必要在deque的前端或尾端增加新空間,便配置一段定量連續空間,串聯在整個deque的頭部或尾部。要明白deque對外提供的是和vector一樣的迭代器類型,所以在內部是很麻煩的。Deque內部是一個指標數組,元素是一段聯絡的記憶體。其實deque對外的封裝所有的操作都是和vector類似的,只不過內部操作很複雜。Deque中的clear函數也是只有一個緩衝區,所有的資料全部被銷毀,回到了deque的最初狀態。
Stack並不被成為容器,更像是配接器,stack所有元素的進出都必須符合“先進先出”的條件,只有stack頂端的元素,才有機會唄外界取用。預設情況下stack底層使用deque來實現的。Stack提供底層實現機制的入口,預設是使用deque
Queue是先進先出的資料結構。預設情況下queue是使用deque最為底層結構的。其實使用list也可以作為queue和stack的底層結構。Queue和stack都不算作容器而是配接器類型。底層預設都是使用deuque
Heap不是STL組件,但是是priorityqueue的助手。
如果使用二叉尋找樹最為優先順序隊列的底層機制,元素的插入和刪除就是Log的層級,顯得小題大做,因為二叉尋找樹的輸入需要隨機性,並且不容易實現,最後優先順序隊列的底層實現選擇完全二叉樹,也就是二元堆積。同時完全二叉樹的儲存右可以使用數組。這麼一來,一個數組和一組heap演算法來插入元素、刪除元素,取極值,將一數組排列成一個heap,最後也即是使用了最大堆或者最小堆來實現了優先順序隊列,裡面使用了heap的演算法操作。儲存就使用vector。關於heap的演算法有push_heap pop_heap sort_heap make_heap 只有這四個函數。
但是這四個函數在構造優先順序隊列時非常重要。優先順序隊列底層預設是使用vector儲存元素,但是使用的是heap系類的函數來處理儲存的資料的。對外封裝的函數有
建構函式priority_queue empty() push() pop() top() 預設使用的最大堆。
STL中的預設鏈表是個雙向鏈表list,同時還提供了一個單向鏈表slist.slist和list的主要差別是前者的迭代器是單向的forward iteratro後者是雙向的bidirectional iterator 他們的插入移除接合等操作並不會造成原有的迭代器失效。有一點提醒一下,對於插入vector和list都是將元素插入到指定迭代器前面。但是對於slist來說有點困難。必須從頭找,所以除了slist起點外附近的地區之外,其他位置上採用insert或erase操作函數都不合適。Slist提供了Insert_after()和erase_after()。
但是要注意,這裡實現的slist,和核心實現的思想是一樣的。忘記說一點,STL中的list是帶有頭結點的雙向鏈表,對於鏈式儲存(包括deque)在調用clear函數後就銷毀了所有的記憶體,對於list還實現了swap函數。還提供了push_front() pop_front().
Slist也是有頭結點的begin()返回的是head的next指標。
總結:對於序列容器,STL中實現了幾種標準的容器,同時還實現了幾個配接器,容器有vector list deque slist(非標準) 配接器有stack queue priority_queue
Vector就是連續的記憶體空間list就是雙向鏈表,deque比較複雜,但是stack 和queue預設都是deque來實現的。Priority_queue儲存元素雖說使用的是vector,但是相應的操作都是和heap相關的。
對於函數來說,比較特殊的就是list,因為他的特殊性,他內部實現了很多本來演算法庫就有的函數,比如sort merge remove。注意deque list slist的clear函數不同尋常