c++順序容器內部實現

c++順序容器內部實現

        我們在使用C++的容器的時候是否想過C++的內部給我們提供的容器內部的實現機制?作爲一個C++開發者瞭解一些實現的內部機制使得能夠更好的把握實現機制。

1、vector內部實現機制?

          vector內部實現時候一個在堆上建立的一維數組,它擁有一段連續的內存空間,並且起始地址不變,因此它能非常好的支持隨即存取,即[]操作符。vector因爲存儲在堆上,所以支持erase( ), resieze()(重新劃分容器容量)等操作;

     vector不用擔心越界當空間不夠用的時候,系統會自動按照一定的比例(對capacity( )大小)進行擴充。在vector序列末尾添加(push_back( ))或者刪除(pop_back( ))對象效率高,在中間進行插入或刪除效率很低,主要是要進行元素的移動和內存的拷貝,原因就在於當內存不夠用的時候要執行重新分配內存,拷貝對象到新存儲區,銷燬old對象,釋放內存等操作,如果對象很多的話,這種操作代價是相當高的。

     爲了減少這種代價,使用vector最理想的情況就是事先知道所要裝入的對象數目,用成員函式 reserve( )預定下來;vector最大的優點莫過於是檢索(用operator[ ])速度在這三個容器中是最快的

a、vector爲了支持快速的隨機訪問,容器中的元素以連續的方式進行存儲;

b、當往容器內添加一個新的元素時,如果容器中沒有空間容納新的元素,此時,由於元素必須連續存儲以便索引訪問,所以不能在內存中隨便找個地方存儲這個心元素。於是,vector必須重新分配存儲空間,用來存放原來的元素以及新添加的元素:存放在舊存儲卡就中的元素被複到新存儲空間裏,藉着插入新元素,最後撤銷舊的存儲空間。爲了實現vector容器的快速內存分配,在實際分配過程中的容量要比當前所需的空間多一些,vector容器預留出這些額外的存儲區,用於存放心添加的元素。於是,不必爲每一個新元素重新分配容器。(以最小的代價連續存儲元素從而帶來訪問元素的遍歷以彌補其存儲空間代價)

c、由於vector進行連續存儲,所以在vector裏面需要定義一個容量capacity的概念,vector的size指的是當前容器中所具有的元素的個數,而capacity則指的是容器在需要重新分配內存空間前所能存儲的元素總數,reserve則表示容器需要預留多少個元素的存儲空間。capacity>=size,標準庫中提供了reserve和capacity函數,程序員可以自己定義額外的存儲空間或者容量。當vector的當前容量已經被消耗完全的時候,容器不得不分配新的存儲空間,以加倍當前容量的分配策略是先重新分配。當然,vector的每種是先可自由選擇內存分配策略,必要時才進行分配,分配多少內存取決於其具體的實現方式。

d、在實現內部容器的地址擴充的時候可以採用擴充固定長度(當前長度的一半)的方式,而不是每次都新增需要增加的元素的長度,這樣可以進一步的提高vector的操作速度。

如下:

1、初始化部分

vector< int > ivec;

cout  << ivec.size() << << ivec,capacity() << endl;

for( vector< int >::size_type ix = 0;ix != 24;++ix) 

    ivec.push_back(i);

cout  << ivec.size() << << ivec,capacity() << endl;

輸出結果: 0  0           24  32

2、預留額外空間

ivec.reserve(50);

cout << ivec.size() << ivec.capacity() << endl;

while(ivec.size() != ivec.capacity())

    ivec.push_back(0);

cout << ivec.size() << << ivec,capacity() << endl;

輸出結果:24  50        50  50

3、重新分配空間

ivec.push_back(42);

cout << ivec.size() << << ivec,capacity() << endl;

輸出結果:51  100

2、list內部實現機制?

 list的本質是一個雙向鏈表,內存空間不連續,通過指針進行操作。因爲是鏈表它的高效率首先表現是插入,刪除元素,進行排序等等需要移動大量元素的操作。顯然鏈表沒有檢索操作operator[ ], 也就是說不能對鏈表進行隨機訪問,而只能從頭至尾地遍歷,這是它的一個缺陷。list有不同於前兩者的某些成員方法,如合併list的方法splice( ), 排序sort( ),交換list 的方法swap( )等等。

a、不支持下標操作

b、當往容器內添加一個新的元素時,標準庫只需要創建一個新的元素,然後將新元素連接在已存在的鏈表中,不需要重新分配存儲空間,也不必複製任何已經存在的元素。

3、deque內部實現機制?

  雙端隊列(deque)是一種支持向兩端高效地插入數據、支持隨機訪問的容器,雙端隊列的數據被表示爲一個分段數組,容器中的元素分段存放在一個個大小固定的數組中,此外容器還需要維護一個存放這些數組首地址的索引數組,如下圖所示。

(轉載圖片)

   由於分段數組的大小是固定的,並且它們的首地址被連續存放在索引數組中,因此可以對其進行隨機訪問,但效率比vector低很多。向兩端加入新元素時,如果這一端的分段數組未滿,則可以直接加入,如果這一端的分段數組已滿,只需創建新的分段數組,並把該分段數組的地址加入到索引數組中即可。無論哪種情況都不需要對已有元素進行移動,因此在雙端隊列的兩端加入新的元素都具有較高的效率。

   當刪除雙端隊列容器兩端的元素時,由於不需要發生元素的移動,效率也是非常高的。雙端隊列中間插入元素時,需要將插入點到某一端之間的所有元素向容器的這一端移動,因此向中間插入元素的效率較低,而且往往插入位置越靠近中間,效率越低。刪除隊列中元素時,情況也類似,由於被刪除元素到某一端之間的所有元素都要向中間移動,刪除的位置越靠近中間,效率越低。

注意:
   在除了首尾兩端的其他地方插入和刪除元素,都將會導致指向deque元素的任何pointers、references、iterators失效。不過,deque的內存重分配優於vector,因爲其內部結構顯示不需要複製所有元素。

4、容器之間的比較?

a、vector和deque支持下標操作,list不支持下標操作

b、vector和deque容器均提供了對元素的快速隨機訪問,但是在容器的任意位置插入或者刪除元素,避免容器尾部插入和刪除開銷更大;而list類型在任何位置都能快速插入和刪除,但是元素的隨機訪問開銷較大

c、deque容器與vector容器在與deque的數據結構更爲複雜,deque容器同時提供了高效的在其首部insert和erase操作,就如同在尾部進行操作一樣

d、在deque容器首尾部插入元素不會使任何迭代器失效,而在首部或者尾部刪除元素則只會使指向被刪除元素的迭代器失效,在其他的任何位置刪除或者插入都會使該容器的所有迭代器失效

發佈了5 篇原創文章 · 獲贊 0 · 訪問量 7847
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章