C++STL的vector源碼分析戈鲁、內(nèi)存管理及問答

標(biāo)準(zhǔn)模板庫由三個(gè)部分組成:容器婆殿、迭代器、算法

Q:容器分為哪幾種抓谴?

A:順序容器寞缝、關(guān)聯(lián)容器荆陆、容器適配器

Q:簡要闡述一下你理解的vector容器

A:它相當(dāng)于一個(gè)會(huì)自動(dòng)增長的數(shù)組。與數(shù)組比較起來帜消,它花費(fèi)更多的內(nèi)存去有效的管理存儲(chǔ)和動(dòng)態(tài)增長浓体。

在問其他問題之前命浴,我們先來理解一下vector的push_back源碼

vector的push_back源碼

1.首先先判斷該值的地址是否位于vector當(dāng)前已有數(shù)據(jù)的地址范圍內(nèi)

2.如果存在,計(jì)算出它與首地址的偏移量媳溺,使用這個(gè)值來初始化數(shù)據(jù)

3.如果不存在悬蔽,那么先查看當(dāng)前容量是否已滿

4.如果沒有可用空間了,重新申請內(nèi)存(reserve)

5.如果有可用空間录语,就使用construct來初始化數(shù)據(jù)

6.最后难衰,尾指針向后移動(dòng)

7.析構(gòu)函數(shù)釋放原有的vector內(nèi)存


下面我們來看一下是如何重新申請內(nèi)存的

1.如果當(dāng)前可使用的容量小于要插入的數(shù)量(1)盖袭,那么就需要重新申請內(nèi)存

2.如果最大容量減去當(dāng)前容量小于1,那么拋出vector過長異常

3.申請內(nèi)存之前弟塞,容量需要按照增長50%或?yàn)椴迦氲臄?shù)量(1拙已,這是在當(dāng)前容量等于0的情況下)

4.申請新內(nèi)存倍踪,將原始空間析構(gòu)函數(shù)釋放,重新計(jì)算頭尾指針的偏移量



Q:分析一下push_back與c11的emplace_back

A:push_back(A("cc")) 此時(shí)它創(chuàng)建一個(gè)臨時(shí)局部A類對象。而emplace_back("cc")可以在內(nèi)存中直接創(chuàng)建對象而不需要傳入對象潮罪。

? 總的來說领斥,再插入值的時(shí)候月洛,push_back會(huì)拷貝元素,emplace_back會(huì)構(gòu)造元素导而。但是當(dāng)插入的是對象的時(shí)候隔崎,emplace_back的形參里的參數(shù)應(yīng)該與對象的構(gòu)造函數(shù)相對應(yīng)


Q:vector的erase函數(shù)是如何實(shí)現(xiàn)的爵卒?

A:因?yàn)関ector是以array為底層實(shí)現(xiàn),那么vector也是一段連續(xù)的代碼塊实牡,所以當(dāng)我們按照某個(gè)位置擦除掉某個(gè)元素/某幾個(gè)元素時(shí)创坞,容器會(huì)重新遷移剩余的元素受葛,使它們依舊緊湊在一起。其中使用了copy算法(傳入要?jiǎng)h除的元素串的尾部纲堵、vector的尾部:注意這不是容量的尾部是大小的尾部席函、傳入要?jiǎng)h除的元素串的尾部)冈涧,這樣就會(huì)把元素串的尾部到vector的尾部的元素串復(fù)制到地址從元素串的首部開始。


vector的resize函數(shù)(size)和reserve函數(shù)(capacity)

c++文檔中reserve函數(shù)有一個(gè)簡短的定義“Request a change in capacity”营曼,這可以看出reserve函數(shù)主要是在容量上進(jìn)行增加溶推。

前面已經(jīng)解釋過了reserve的源碼奸攻,現(xiàn)在來看一看resize的源碼

1.如果傳入的新的大小大于舊的大小睹耐,那么在舊的大小的尾部插入長度為它們之間差值的元素串。

2.如果小于舊的大小响委,那么把舊的大小大于新大小的那一部分的元素刪去

所以resize是改變size大凶阜纭(有可能改變capacity的大小,當(dāng)需要擴(kuò)容的時(shí)候)荸哟,而reserve是改變capacity大小瞬捕。

那么它們哪個(gè)性能更優(yōu)呢鞍历?

因?yàn)閞esize可能會(huì)改變capacity的大小,那么這個(gè)時(shí)候它就需要擴(kuò)容肪虎,擴(kuò)容的關(guān)鍵就是新建一個(gè)vector對象劣砍,再將原本的數(shù)據(jù)拷貝進(jìn)入該對象中,再把要插入的值寫入扇救。因?yàn)樾聞?chuàng)建了對象所以它的性能會(huì)比較低下刑枝。reserve表示容器預(yù)留空間,在改變capacity大小時(shí)爵政,因?yàn)闆]有給該內(nèi)存進(jìn)行初始化仅讽,所以不會(huì)去新建對象,所以它不能引用容器內(nèi)的元素洁灵,如果需要引用就需要使用push_back和insert。

如果還不理解它們之間的關(guān)系掺出,這里有一個(gè)小例子:

? ? ? ? ? ? 正在建造的一輛公交車徽千,車?yán)锩婵梢栽O(shè)置40個(gè)座椅(reserve(40);),這是它的容量汤锨,但并不是說它里面就有了40個(gè)座椅双抽,只能說明這部車內(nèi)部空間大小可以放得下40張座椅而已。而車?yán)锩姘惭b了40個(gè)座椅(resize(40);)闲礼,這個(gè)時(shí)候車?yán)锩娌耪嬲辛?0個(gè)座椅牍汹,這些座椅就可以使用了


總的來說,reserve(預(yù)留空間柬泽、改變內(nèi)存慎菲、不建對象)

? ? resize(改變size、可能改變內(nèi)存锨并、新建對象)

Q:vector的大新陡谩(vector::size)與它的容量(vector::capacity)是一樣的嗎?

A:不一樣第煮。當(dāng)vector有特定容量且vector數(shù)據(jù)已經(jīng)存滿的時(shí)候解幼,它們的大小和容量是相同的抑党。但是如果此時(shí)再插入一個(gè)數(shù)據(jù),容量會(huì)按照一定的增長方式增加撵摆,此時(shí)大小就會(huì)小于容量底靠。

Q:詳細(xì)描述一下vector的內(nèi)存管理

A:為了避免每一次插入都要擴(kuò)容,vector在初始化時(shí)會(huì)讓容量大于預(yù)裝入的數(shù)據(jù)長台汇。

? ? ? 當(dāng)插入數(shù)據(jù)的時(shí)候苛骨,如果此時(shí)容量已滿篱瞎,那么就實(shí)現(xiàn)擴(kuò)容(擴(kuò)容并不是在原vector的基礎(chǔ)上苟呐,而是新建一個(gè)大小是舊容量的150%的新vector,然后再將舊vector的值拷貝到新vector俐筋,再將插入的值拷貝進(jìn)vector牵素。然后調(diào)用析構(gòu)函數(shù)釋放舊vector的內(nèi)存)。

Q:erase后的vector澄者,它的容量依舊是不變的笆呆,只是存儲(chǔ)的數(shù)據(jù)變少了,所以如果存在容量100000的vector粱挡,釋放了99999的數(shù)據(jù)赠幕,那么此時(shí)它的容量依舊一樣,而有效size只剩1询筏。那么如何強(qiáng)制釋放vector的緩沖區(qū)榕堰?

A:方法1:std::vector<int>().swap(arr)? arr是待釋放的vector

? ? ? 解釋:首先vector()使用默認(rèn)構(gòu)造函數(shù)建立了一個(gè)臨時(shí)的vector對象,當(dāng)這個(gè)臨時(shí)對象調(diào)用swap時(shí)嫌套,arr的大小變?yōu)槟J(rèn)構(gòu)造對象的大小逆屡,而臨時(shí)對象的大小為arr的大小,因?yàn)榕R時(shí)對象很快就會(huì)通過析構(gòu)函數(shù)被釋放掉踱讨,所以占用的空間就被釋放了魏蔗。

? ? 方法2:<vector> int temp; arr.swap(temp);

? ? ? 解釋:此時(shí)temp為臨時(shí)對象,大小為0痹筛,arr與temp相交換莺治,arr的緩沖區(qū)就變?yōu)?了。因?yàn)榕R時(shí)變量會(huì)被析構(gòu)帚稠,所以占用的空間也被釋放了谣旁。

Q:vector的容量過小后,是如何實(shí)現(xiàn)擴(kuò)增容量的翁锡?

A:先按照一定的增長方式增加容量大小蔓挖,重新分配一個(gè)符合該大小的vector,再將原本vector的數(shù)組拷貝進(jìn)新的vector馆衔,再插入新的值瘟判。因?yàn)槊恳淮沃匦路峙鋠ector和拷貝十分耗時(shí)怨绣,所以vector并不會(huì)在每次增加新數(shù)據(jù)時(shí)都重新分配,那么這就意味著我們需要定義一個(gè)比我們預(yù)期存儲(chǔ)范圍還大出一部分(這一部分用于可能的范圍增長)的vector拷获,這樣就可以避免每次重新分配vector篮撑。

Q:在多線程時(shí),對vector寫時(shí)可能會(huì)出現(xiàn)什么錯(cuò)誤匆瓜?

A:程序崩潰赢笨,因?yàn)榫€程A vector進(jìn)行寫時(shí),如果內(nèi)存已滿會(huì)重新申請內(nèi)存驮吱,此時(shí)它的地址已經(jīng)改變茧妒,而線程B依舊在寫入/讀入已經(jīng)無效的地址,就會(huì)造成崩潰左冬。

有兩種解決辦法:1.暴力解決法:直接給vector定義一個(gè)較大的內(nèi)存桐筏,避免重新申請

? ? ? ? ? ? ? ? ? ? ? ? ? ? 2.加同步鎖,每次寫入前都鎖住拇砰,執(zhí)行完本次寫入操作后梅忌,其他線程才能再次寫入或讀入。

所以除破!對容器的modify操作應(yīng)該是原子性(一旦操作開始牧氮,到結(jié)束之前都不會(huì)切換到其他線程)的!

Q:我們使用sort算法時(shí)瑰枫,傳入容器的起始及結(jié)束踱葛。一般在沒有特殊約束下,sort都是由小到大排列躁垛,那么如何讓它由大到小排列剖毯?

A:sort還有另外一個(gè)函數(shù),存在第三個(gè)形參教馆,可以傳入謂詞逊谋。謂詞有很多種,比如書上有寫到的庫定義的函數(shù)謂詞對象土铺,也就是std::greater<int>() 胶滋,將它作為第三個(gè)形參傳入,就可以實(shí)現(xiàn)由大到小悲敷。還有很多種實(shí)現(xiàn)方式(函數(shù)謂詞究恤,函數(shù)指針謂詞,函數(shù)對象謂詞后德,lambda表達(dá)式

Q:vector里的重要函數(shù)

A:push_back? pop_back? erase? clear? insert

Q:vector如何進(jìn)行初始化

A:1.直接賦值

? ? vector<int> num(20,3)包含20個(gè)值為3的數(shù)

? ? 2.使用數(shù)組進(jìn)行遍歷賦值

Q:vector如何訪問元素

A:可以通過迭代器訪問元素部宿、下標(biāo)訪問、at(i)訪問

Q:簡要闡述一下deque

A:和vector類似,但是它可以在隊(duì)列的首部和尾部添加或刪除元素(push_front? pop_front)理张,因?yàn)樗鼉?nèi)存管理比較復(fù)雜赫蛇,所以執(zhí)行速度慢于

Q:簡要闡述一下list容器

A:適合頻繁插入刪除元素,但是查找比較復(fù)雜雾叭。除了和vector一樣可以正向遍歷之外悟耘,還能使用rbegin rend反向遍歷,和deque一樣的就是也含有push_front pop_front等

Q:映射容器map可以用兩種方法插入织狐,一種是insert暂幼,一種是下標(biāo)運(yùn)算符,插入有重復(fù)鍵值的數(shù)據(jù)時(shí)移迫,它們的效果是否相同旺嬉?

A:不相同,insert會(huì)插入失敗起意,而下標(biāo)運(yùn)算符會(huì)覆蓋以前重復(fù)鍵值對應(yīng)的對象

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末鹰服,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子揽咕,更是在濱河造成了極大的恐慌,老刑警劉巖套菜,帶你破解...
    沈念sama閱讀 222,865評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件亲善,死亡現(xiàn)場離奇詭異,居然都是意外死亡逗柴,警方通過查閱死者的電腦和手機(jī)蛹头,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,296評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來戏溺,“玉大人渣蜗,你說我怎么就攤上這事】趸觯” “怎么了耕拷?”我有些...
    開封第一講書人閱讀 169,631評論 0 364
  • 文/不壞的土叔 我叫張陵,是天一觀的道長托享。 經(jīng)常有香客問我骚烧,道長,這世上最難降的妖魔是什么闰围? 我笑而不...
    開封第一講書人閱讀 60,199評論 1 300
  • 正文 為了忘掉前任赃绊,我火速辦了婚禮,結(jié)果婚禮上羡榴,老公的妹妹穿的比我還像新娘碧查。我一直安慰自己,他們只是感情好校仑,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,196評論 6 398
  • 文/花漫 我一把揭開白布忠售。 她就那樣靜靜地躺著者冤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪档痪。 梳的紋絲不亂的頭發(fā)上涉枫,一...
    開封第一講書人閱讀 52,793評論 1 314
  • 那天,我揣著相機(jī)與錄音腐螟,去河邊找鬼愿汰。 笑死,一個(gè)胖子當(dāng)著我的面吹牛乐纸,可吹牛的內(nèi)容都是我干的衬廷。 我是一名探鬼主播,決...
    沈念sama閱讀 41,221評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼汽绢,長吁一口氣:“原來是場噩夢啊……” “哼吗跋!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起宁昭,我...
    開封第一講書人閱讀 40,174評論 0 277
  • 序言:老撾萬榮一對情侶失蹤跌宛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后积仗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體疆拘,經(jīng)...
    沈念sama閱讀 46,699評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,770評論 3 343
  • 正文 我和宋清朗相戀三年寂曹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了哎迄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,918評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡隆圆,死狀恐怖漱挚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情渺氧,我是刑警寧澤旨涝,帶...
    沈念sama閱讀 36,573評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站阶女,受9級特大地震影響颊糜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜秃踩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,255評論 3 336
  • 文/蒙蒙 一衬鱼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧憔杨,春花似錦鸟赫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,749評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽台谢。三九已至,卻和暖如春岁经,著一層夾襖步出監(jiān)牢的瞬間朋沮,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,862評論 1 274
  • 我被黑心中介騙來泰國打工缀壤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留樊拓,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,364評論 3 379
  • 正文 我出身青樓塘慕,卻偏偏與公主長得像筋夏,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子图呢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,926評論 2 361

推薦閱讀更多精彩內(nèi)容