日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > c/c++ >内容正文

c/c++

容器大小_C++ 顺序容器基础知识总结

發(fā)布時(shí)間:2023/12/15 c/c++ 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 容器大小_C++ 顺序容器基础知识总结 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

閱讀目錄

  • 0.前言
  • 1.容器概論
  • 2.std::array
  • 2.1.底層數(shù)據(jù)結(jié)構(gòu)
  • 2.2.內(nèi)存分配策略
  • 2.3.array的優(yōu)勢(shì)在哪
  • 3.forward_list
  • 3.1.底層數(shù)據(jù)結(jié)構(gòu)
  • 3.2.forward_list特殊之一:forward_list不提供返回其大小的操作。
  • 3.3.forward_list特殊之二:forward_list是唯一一個(gè)在給定位置之后插入新元素的容器。
  • 3.4.迭代器失效問(wèn)題
  • 4.list
  • 4.1.底層數(shù)據(jù)結(jié)構(gòu)
  • 4.2.迭代器類(lèi)型
  • 4.3.內(nèi)存分配策略
  • 4.4.迭代器失效問(wèn)題
  • 5.vector
  • 5.1.底層數(shù)據(jù)結(jié)構(gòu)
  • 5.2.迭代器類(lèi)型
  • 5.3.內(nèi)存分配策略
  • 5.4.迭代器失效問(wèn)題
  • 6.deque
  • 6.1.底層數(shù)據(jù)結(jié)構(gòu)
  • 6.2.內(nèi)存分配策略
  • 6.3.deque的迭代器
  • 6.4.迭代器失效問(wèn)題
  • 7.容器適配器
  • 8.總結(jié)

0、前言

本文簡(jiǎn)單地總結(jié)了STL的順序容器的知識(shí)點(diǎn)。文中并不涉及具體的實(shí)現(xiàn)技巧,對(duì)于細(xì)節(jié)的東西也沒(méi)有提及。一來(lái)不同的標(biāo)準(zhǔn)庫(kù)有著不同的實(shí)現(xiàn),二來(lái)關(guān)于具體實(shí)現(xiàn)《STL源碼剖析》已經(jīng)展示得全面細(xì)致。所以本文僅僅是對(duì)容器基礎(chǔ)知識(shí)的歸納。至于容器提供的接口與使用實(shí)例,建議查取官方文檔。文章難免有錯(cuò)漏,希望指出。

1、容器概論

容器,置物之所也。像桶可裝水,碗可盛湯,C++的容器,可以存儲(chǔ)對(duì)象。容器有多種,用來(lái)處理不同的元素操作訴求。按照元素存儲(chǔ)到容器中以及訪問(wèn)方式的差異,容器分為順序容器與關(guān)聯(lián)容器。順序容器也稱(chēng)為序列式容器。序列式容器按元素插入的順序存儲(chǔ)元素,這些元素可以進(jìn)行排序,但未必是有序的。C++本身內(nèi)置了一個(gè)序列式容器array(數(shù)組),STL另外提供了vector,list,forward_list,deque,stack,queue,priority-queue,string等等序列式容器。所有的容器都是基于模板實(shí)現(xiàn)的,因?yàn)槿萜鞅仨毐WC能裝得下各種各樣的類(lèi)型。其中,stack,queue都是基于deque來(lái)實(shí)現(xiàn)的,priority-queue基于heap來(lái)實(shí)現(xiàn),從技術(shù)上來(lái)說(shuō)它們屬于容器適配器(adapter)。其中array與forward_list是C++11添加的新容器類(lèi)型。

2、std::array

2.1.底層數(shù)據(jù)結(jié)構(gòu)

array的底層數(shù)據(jù)結(jié)構(gòu)是固定數(shù)組。與C-style的數(shù)組類(lèi)似,它的大小在定義后就不能被改變。由于array具有固定的大小,它不支持添加和刪除元素或改變?nèi)萜鞔笮〉绕渌萜鲹碛械牟僮鳌T诙x一個(gè)array容器的時(shí)候必須指定大小:

Defined in header template< class T, std::size_t N> struct array;

2.2.內(nèi)存分配策略

在內(nèi)存分配策略上,array也與C-style數(shù)組類(lèi)似。編譯器在哪里為array分配內(nèi)存,取決于array定義的位置和方式。

  • 若作為函數(shù)的局部對(duì)象,則將從棧上獲得內(nèi)存,與之對(duì)比是的vector,vector底層數(shù)據(jù)結(jié)構(gòu)是動(dòng)態(tài)數(shù)組,從自由存儲(chǔ)區(qū)上分配內(nèi)存:
  • 若使用new操作符分配內(nèi)存,則是在自由存儲(chǔ)區(qū)上分配內(nèi)存。
  • 若作為全局變量或局部靜態(tài)變量,則是在全局/靜態(tài)存儲(chǔ)區(qū)上分配的內(nèi)存。

例如,在函數(shù)定義的array局部對(duì)象在棧上分配內(nèi)存,與此對(duì)比的是vector,它底層數(shù)據(jù)結(jié)構(gòu)為動(dòng)態(tài)數(shù)組,因此在自由存儲(chǔ)區(qū)上分配內(nèi)存:

#include

#include

#include

using namespace std;

int main() {

int stack_var;

array a;

vector b(5);

cout << "stack area: 0x" << hex << (uintptr_t)&stack_var << endl;

cout << "a[3] address: 0x" << hex << (uintptr_t)&a[3] << endl;

cout << "b[3] address: 0x" << hex << (uintptr_t)&b[3] << endl;

getchar();

return 0;

}

結(jié)果:

2.3.array的優(yōu)勢(shì)在哪

  • array比數(shù)組更安全。它提供了opeartor[]與at()成員函數(shù),后者將進(jìn)行數(shù)組越界檢查。
  • 與其他容器相似,array也有自己的迭代器,因此array能夠更好地與標(biāo)準(zhǔn)算法庫(kù)結(jié)合起來(lái)。
  • 通過(guò)array::swap函數(shù),可以實(shí)現(xiàn)線性時(shí)間內(nèi)的兩個(gè)數(shù)組內(nèi)容的交換。

另外,不像C-style數(shù)組,array容器類(lèi)型的名稱(chēng)不會(huì)自動(dòng)轉(zhuǎn)換為指針。對(duì)于C++程序員來(lái)說(shuō),array要比C-style數(shù)組更好用。

3、forward_list

3.1.底層數(shù)據(jù)結(jié)構(gòu)

forward_list的底層數(shù)據(jù)結(jié)構(gòu)為單向鏈表。如C++標(biāo)準(zhǔn)所講,forward_list容器支持前向遍歷元素序列,允許常數(shù)時(shí)間內(nèi)在任意位置的插入或刪除操作并進(jìn)行自動(dòng)的內(nèi)存管理。與list的主要區(qū)別是forward_list沒(méi)有反方向的迭代器,不過(guò)也正因如此,forward_list的每個(gè)節(jié)點(diǎn)都節(jié)省了迭代器大小的開(kāi)銷(xiāo),在元素眾多的時(shí)候,將比list消耗少得多的內(nèi)存。

受單向鏈表這種特殊結(jié)構(gòu)的影響,forward_list在很多地方表現(xiàn)得和其他容器不同:

3.2.forward_list特殊之一:forward_list不提供返回其大小的操作。

在所有已知的STL容器中,forward_list是唯一一個(gè)不提供size()的容器。不提供的原因在于計(jì)算一個(gè)forward_list的長(zhǎng)度需要線性的時(shí)間,庫(kù)用戶有時(shí)無(wú)法忍受這樣的時(shí)間開(kāi)銷(xiāo)。其他容器提供的size()操作皆可以在常數(shù)時(shí)間內(nèi)完成(在C++98時(shí),list也是線性時(shí)間)。為了節(jié)省內(nèi)存,forward_list甚至不跟蹤序列的長(zhǎng)度,要想獲得某個(gè)forward_list對(duì)象的長(zhǎng)度,用戶需要通過(guò)distance()來(lái)計(jì)算。這帶來(lái)了一些不便,但使得用戶遠(yuǎn)離了size()帶來(lái)的高消耗。每個(gè)容器類(lèi)型都有三個(gè)與大小相關(guān)的操作:

max_size(),empty(),size(),而forward_list只提供了前兩個(gè)。

int main()

{

forward_list flist;

for (int i = 0; i < 10; i++)

{

flist.push_front(i);

}

std::cout << std::distance(flist.begin(), flist.end()); //輸出10

getchar();

}

3.3.forward_list特殊之二:forward_list是唯一一個(gè)在給定位置之后插入新元素的容器。

為此,forward_list提供了如下的插入接口:

接口描述insert_after在給定位置之后插入新元素emplace_after在給定位置之后構(gòu)造新元素erase_after刪除給定位置之后的元素splice_after將另一個(gè)forward_list的元素移動(dòng)到本forward_list的指定位置之后

其他所有STL容器都是在指定位置之前插入元素(除了std::array,它不允許插入)。forward_list的這種特殊處理,還是出于效率的考慮。對(duì)于單鏈表我們應(yīng)該很熟悉,為了在某個(gè)指定節(jié)點(diǎn)之前插入插入節(jié)點(diǎn),我們必須改變插入位置的前一個(gè)節(jié)點(diǎn)的指向。換句話說(shuō),為了在指定節(jié)點(diǎn)之前插入新元素,我們必須要先獲得插入位置前一個(gè)位置的節(jié)點(diǎn),為了獲取前面這個(gè)節(jié)點(diǎn),需要線性的操作時(shí)間。

而如果我們是在指定位置之后插入新元素,則無(wú)需線性時(shí)間的查找操作,這樣可實(shí)現(xiàn)常數(shù)時(shí)間的插入:

同樣的,處于性能的考慮,forward_list沒(méi)有提供在尾部進(jìn)行操作的接口,包括push_back(),pop_back()和emplace_back(),這些操作對(duì)單列表來(lái)說(shuō)都至少要花費(fèi)O(n)來(lái)完成。

3.4.迭代器失效問(wèn)題

指向被刪除元素的迭代器,在刪除之后失效。

4、list

4.1.底層數(shù)據(jù)結(jié)構(gòu)

list同樣是一個(gè)模板類(lèi),它底層數(shù)據(jù)結(jié)構(gòu)為雙向循環(huán)鏈表。因此,它支持任意位置常數(shù)時(shí)間的插入/刪除操作,不支持快速隨機(jī)訪問(wèn)。

4.2、迭代器類(lèi)型

list的迭代器具備前移、后移的能力,所以list提供的是Bidirectional iterator(雙向迭代器)。由于采用的是雙向迭代器,自然也很方便在指定元素之前插入新節(jié)點(diǎn),所以list很正常地提供了insert()操作與push_back()/pop_back()操作。在C++11中,list新增了三個(gè)接口,以支持在指定位置構(gòu)造對(duì)象后插入容器中:

接口(C++11新增)描述emplace在指定位置之前插入新構(gòu)造的元素emplace_front在鏈表頭插入新構(gòu)造的元素emplace_back在鏈表尾插入新構(gòu)造的元素

4.3.內(nèi)存分配策略

list的空間配置策略,自然是像我們普通雙向鏈表那樣,有多少元素申請(qǐng)多少內(nèi)存。它不像vactor那樣需要預(yù)留空間供新元素的分配,也不會(huì)因找不到連續(xù)的空間而引起整個(gè)容器的內(nèi)存遷移。

4.4.迭代器失效問(wèn)題

list 有一個(gè)重要性質(zhì):插入操作(insert)與接合操作(splice)都不會(huì)造成原有的list迭代器失效。這在vector是不成立的,因?yàn)関actor的插入可能引起空間的重新配置,導(dǎo)致原來(lái)的迭代器全部失效。list的迭代器失效,只會(huì)出現(xiàn)在刪除的時(shí)候,指向刪除元素的那個(gè)迭代器在刪除后失效。

通常來(lái)說(shuō),forward_list在使用靈活度上比不上list,因?yàn)樗荒軉蜗虻?#xff0c;且提供的接口沒(méi)有l(wèi)ist多。然而,在內(nèi)存的使用上,它是比list占優(yōu)勢(shì)的。當(dāng)對(duì)內(nèi)存的要求占首要位置時(shí),應(yīng)該選擇forward_list。

5、vector

5.1.底層數(shù)據(jù)結(jié)構(gòu)

vector的底層數(shù)據(jù)結(jié)構(gòu)是動(dòng)態(tài)數(shù)組,因此,vector的數(shù)據(jù)安排以及操作方式與std::array十很相似,它們間的唯一差別在于對(duì)空間的運(yùn)用靈活性上。array為靜態(tài)數(shù)組,有著靜態(tài)數(shù)組最大的缺點(diǎn):每次只能分配一定大小的存儲(chǔ)空間,當(dāng)有新元素插入時(shí),要經(jīng)歷 “找到更大的內(nèi)存空間”->“把數(shù)據(jù)復(fù)制到新空間” ->“銷(xiāo)毀舊空間” 三部曲, 對(duì)于std::array而言,這種空間管理的任務(wù)壓在使用它的用戶身上,用戶必須把握好數(shù)據(jù)的數(shù)量,盡量在第一次分配時(shí)就給數(shù)據(jù)分配合理的空間(這有時(shí)很難做到),以防止“三部曲”帶來(lái)的代價(jià),而數(shù)據(jù)溢出也是靜態(tài)數(shù)組使用者需要注意的問(wèn)題。而vector用戶不需要親自處理空間運(yùn)用問(wèn)題。vector是動(dòng)態(tài)空間,隨著新元素的插入,舊存儲(chǔ)空間不夠用時(shí),vector內(nèi)部機(jī)制會(huì)自行擴(kuò)充空間以容納新元素,當(dāng)然,這種空間擴(kuò)充大部分情況下(幾乎是)也逃脫不了“三部曲”,只是不需要用戶自己處理,而且vector處理得更加安全高效。vector的實(shí)現(xiàn)技術(shù)關(guān)鍵就在于對(duì)其大小的控制以及重新配置時(shí)數(shù)據(jù)移動(dòng)效率。

5.2.迭代器類(lèi)型

對(duì)于C_style數(shù)組,我們使用普通指針就可以對(duì)數(shù)組進(jìn)行各種操作。vector維護(hù)的是一個(gè)連續(xù)線性空間,與數(shù)組一樣,所以無(wú)論其元素型別為何,普通指針都可以作為vector的迭代器而滿足所有必要的條件。vector所需要的迭代器操作,包括operator*,operator->,operator++,operator--,operator+=,operator-=等,普通指針都具有。因此,普通指針即可滿足vector對(duì)迭代器的需求。所以,vector提供了Random Access Iterators。

5.3.內(nèi)存分配策略

標(biāo)準(zhǔn)庫(kù)的實(shí)現(xiàn)者使用了這樣的內(nèi)存分配策略:以最小的代價(jià)連續(xù)存儲(chǔ)元素。為了使vector容器實(shí)現(xiàn)快速的內(nèi)存分配,其實(shí)際分配的容量要比當(dāng)前所需的空間多一些(預(yù)留空間),vector容器預(yù)留了這些額外的存儲(chǔ)區(qū)用于存放添加的新元素,于是不必為每個(gè)新元素進(jìn)行一次內(nèi)存分配。當(dāng)繼續(xù)向容器中加入元素導(dǎo)致備用空間被用光(超過(guò)了容量 capacity),此時(shí)再加入元素時(shí)vector的內(nèi)存管理機(jī)制便會(huì)擴(kuò)充容量至兩倍,如果兩倍容量仍不足,就擴(kuò)張至足夠大的容量。容量擴(kuò)張必須經(jīng)歷“重新配置、元素移動(dòng)、釋放原空間”這個(gè)浩大的工程。按照《STL源碼剖析》中提供的vector源碼,vector的內(nèi)存配置原則為:

  • 如果vector原大小為0,則配置1,也即一個(gè)元素的大小。
  • 如果原大小不為0,則配置原大小的兩倍。

當(dāng)然,vector的每種實(shí)現(xiàn)都可以自由地選擇自己的內(nèi)存分配策略,分配多少內(nèi)存取決于其實(shí)現(xiàn)方式,不同的庫(kù)采用不同的分配策略。

5.4.迭代器失效問(wèn)題

1、vector管理的是連續(xù)的內(nèi)存空間,在容器中插入(或刪除)元素時(shí),插入(或刪除)點(diǎn)后面的所有元素都需要向后(或向前)移動(dòng)一個(gè)位置,指向發(fā)生移動(dòng)的元素的迭代器都失效。這里以插入操作示例:

2、隨著元素的插入,原來(lái)分配的連續(xù)內(nèi)存空間已經(jīng)不夠且無(wú)法在原地拓展新的內(nèi)存空間,整個(gè)容器會(huì)被copy到另外一塊內(nèi)存上,此時(shí)指向原來(lái)容器元素的所有迭代器通通失效。

3、刪除元素后,指向被刪除元素的迭代器失效,這是顯而易見(jiàn)的。

6、deque

6.1.底層數(shù)據(jù)結(jié)構(gòu)

vector是單向開(kāi)口的線性連續(xù)空間,deque則是一種雙向開(kāi)口的連續(xù)數(shù)據(jù)空間。所謂的雙向開(kāi)口,意思是可以在頭尾兩端分別做元素的插入和刪除操作。當(dāng)然vector也可以在頭尾兩端進(jìn)行操作,但是其頭部操作效果奇差,所以標(biāo)準(zhǔn)庫(kù)沒(méi)有為vector提供push_front或pop_front操作。與vector類(lèi)似,deque支持元素的快速隨機(jī)訪問(wèn)。deque的示意圖如下:

現(xiàn)在問(wèn)題來(lái)了:如果deque以數(shù)組來(lái)實(shí)現(xiàn),如何做到在頭部的常數(shù)時(shí)間插入?如果是采用鏈表來(lái)實(shí)現(xiàn),又如何做到快速隨機(jī)訪問(wèn)?deque的內(nèi)部數(shù)據(jù)結(jié)構(gòu)到底如何?想必你已經(jīng)猜到了,要實(shí)現(xiàn)如上需求,需要由一段一段的連續(xù)空間鏈接起來(lái)的數(shù)據(jù)結(jié)構(gòu)才能滿足。

6.2.內(nèi)存分配策略

接著上面講。deque由一段一段的連續(xù)空間所鏈接而成,一旦需要在deque的前端或尾端增加新空間,便配置一段定量的連續(xù)空間,并將該空間串接在deque的頭部或尾部。deque復(fù)雜的迭代器架構(gòu),構(gòu)建出了所有分段連續(xù)空間”整體連續(xù)“的假象。

既然deque是由一段一段定長(zhǎng)的連續(xù)空間所構(gòu)成,就需要有結(jié)構(gòu)來(lái)管理這些連續(xù)空間。deque采用一塊map(非STL中的map)作為主控,map是一塊小的連續(xù)空間,其中每個(gè)元素都是指針,指向一塊較大的線性連續(xù)空間,稱(chēng)為緩沖區(qū)。而緩沖區(qū)才是存儲(chǔ)deque元素的空間主體。示例圖:

map本身也是一塊固定大小的連續(xù)空間,當(dāng)緩沖區(qū)數(shù)量增多,map容不下更多的指針時(shí),deque會(huì)尋找一塊新的空間來(lái)作為map。

6.3.deque的迭代器

為了使得這些分段的連續(xù)空間看起來(lái)像是一個(gè)整體,deque的迭代器必須有這樣的能力:它必須能夠指出分段連續(xù)空間在哪里,判斷自己所指的位置是否位于某一個(gè)緩沖區(qū)的邊緣,如果位于邊緣,則執(zhí)行operator-- 或operator++時(shí)要能夠自動(dòng)跳到下一個(gè)緩沖區(qū)。因此,盡管deque的迭代器也是Ramdon Access Iterator 迭代器,但它的實(shí)現(xiàn)要比vector的復(fù)雜太多。SGI版本的STL deque實(shí)現(xiàn)思路可以看侯捷的《STL源碼剖析》。

6.4.迭代器失效問(wèn)題

  • 在deque容器首部或者尾部插入元素不會(huì)使得任何迭代器失效。
  • 在其首部或尾部刪除元素則只會(huì)使指向被刪除元素的迭代器失效。
  • 在deque容器的任何其他位置的插入和刪除操作將使指向該容器元素的所有迭代器失效。

7、容器適配器

stack,也稱(chēng)為棧,是一種先進(jìn)后出的數(shù)據(jù)結(jié)構(gòu)。STL中的statck是一種容器適配器。所謂的容器適配器,是以某種容器作為底部容器,在底部容器之上修改接口,形成另一種風(fēng)貌。stack默認(rèn)以雙端隊(duì)列deque作為底部容器。stack沒(méi)有提供迭代器,通過(guò)push/pop接口對(duì)棧頂元素進(jìn)行操作。

queue,也稱(chēng)為隊(duì)列,是一種先進(jìn)先出的數(shù)據(jù)結(jié)構(gòu),它同樣也是一種容器適配器。它的底部容器默認(rèn)為deque。同樣,queue也沒(méi)有提供迭代器,通過(guò)push向隊(duì)尾壓入元素,pop從隊(duì)首彈出元素。

priority-queue,優(yōu)先隊(duì)列,是一種擁有權(quán)值觀念的隊(duì)列,例如在以整數(shù)大小作為衡量的權(quán)值定義下,priority-queue總是彈出最大的數(shù)。priority-queue的底部數(shù)據(jù)結(jié)構(gòu)默認(rèn)是max-heap,大頂堆。

8、總結(jié)

容器底層數(shù)據(jù)結(jié)構(gòu)元素訪問(wèn)方式插入或刪除元素效率迭代器失效情況array固定大小的數(shù)組支持快速隨機(jī)訪問(wèn)不能添加或刪除元素通常不會(huì)發(fā)生迭代器失效,除非對(duì)象已經(jīng)被銷(xiāo)毀,則原來(lái)的迭代器全部失效vector可動(dòng)態(tài)增長(zhǎng)的數(shù)組支持快速隨機(jī)訪問(wèn)尾部可高效插入/刪除元素若插入操作引起內(nèi)存重新分配,則全部迭代器失效;否則插入點(diǎn)/刪除點(diǎn)之后的迭代器失效;list雙向鏈表只支持元素的雙向順序訪問(wèn)在list的任何位置可高效插入/刪除元素插入操作后指向容器的迭代器有效;刪除操作指向其他位置的迭代器有效deque雙端隊(duì)列支持快速隨機(jī)訪問(wèn)首尾可高效插入/刪除元素情況較多,見(jiàn)上面分析forward_list單向鏈表只支持元素的單向順序訪問(wèn)在鏈表的任何位置可高效插入/刪除元素插入操作后指向容器的迭代器有效;刪除操作指向其他位置的迭代器有效string只存儲(chǔ)字符元素的動(dòng)態(tài)數(shù)組支持快速隨機(jī)訪問(wèn)尾部可高效插入/刪除元素若插入操作引起內(nèi)存重新分配,則全部迭代器失效;否則插入點(diǎn)/刪除點(diǎn)之后的迭代器失效;stack默認(rèn)deque先進(jìn)后出,只能訪問(wèn)棧頂元素----沒(méi)有迭代器queue默認(rèn)deque先進(jìn)先出,只能訪問(wèn)隊(duì)首元素----沒(méi)有迭代器priority-queue默認(rèn)max-heap先進(jìn)先出,只能訪問(wèn)隊(duì)首元素----沒(méi)有迭代器

注意:

  • “尾部可高效插入/刪除元素”,意味著在除了尾部之外的其他位置插入/刪除元素是較低效的。
  • “順序訪問(wèn)”意味著要訪問(wèn)某一個(gè)元素,必須遍歷其他元素。
  • 迭代器失效意味著指針、引用在同樣的情況下也會(huì)失效。

總結(jié)

以上是生活随笔為你收集整理的容器大小_C++ 顺序容器基础知识总结的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。