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

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

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

《STL源码剖析》相关面试题总结

發(fā)布時(shí)間:2023/12/20 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 《STL源码剖析》相关面试题总结 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一、STL簡(jiǎn)介

STL提供六大組件,彼此可以組合套用:

  • 容器
    容器就是各種數(shù)據(jù)結(jié)構(gòu),我就不多說(shuō),看看下面這張圖回憶一下就好了,從實(shí)現(xiàn)角度看,STL容器是一種class template。
  • 算法
    各種常見算法,如sort,search,copy,erase等,我覺得其中比較值得學(xué)習(xí)的就是sort,next_permutation,partition,merge sort,從實(shí)現(xiàn)角度看,STL算法是一種function template。
  • 迭代器
    扮演容器與算法之間的膠合劑,是所謂的“泛型指針”。共有五種類型,從實(shí)現(xiàn)角度看,迭代器是一種將operator*,operator->,operator++,operator--等指針相關(guān)操作予以重載的class template。所有STL容器都附帶有自己專屬的迭代器,只有容器設(shè)計(jì)者才知道如何設(shè)計(jì)迭代器。原生指針也是一種迭代器。是設(shè)計(jì)模式的一種,所以被問(wèn)到了解的設(shè)計(jì)模式可以用來(lái)湊數(shù)。
  • 仿函數(shù)
    行為類函數(shù),可作為算法的某種策略,從實(shí)現(xiàn)角度看,仿函數(shù)是一種重載了operator()的class或class template。一般函數(shù)指針可視為狹義的仿函數(shù)。
  • 配接器
    一種用來(lái)修飾容器或者仿函數(shù)或迭代器接口的東西。比如queue和stack,看著像容器,其實(shí)就是deque包了一層皮。
  • 配置器
    負(fù)責(zé)空間配置與管理。從實(shí)現(xiàn)角度看,配置器是一個(gè)實(shí)現(xiàn)了動(dòng)態(tài)空間配置、空間管理、空間釋放額class template。
  • 二、關(guān)于容器的一些問(wèn)題

    2.1 當(dāng)vector的內(nèi)存用完了,它是如何動(dòng)態(tài)擴(kuò)展內(nèi)存的?它是怎么釋放內(nèi)存的?用clear可以釋放掉內(nèi)存嗎?是不是線程安全的?

  • vector內(nèi)存用完了,會(huì)以當(dāng)前size大小重新申請(qǐng)2*size的內(nèi)存,然后把原來(lái)的元素復(fù)制過(guò)去,把新元素插上,然后釋放原來(lái)的內(nèi)存。
  • 一般我們釋放vector里的元素使用clear,其實(shí)它不能釋放內(nèi)存,要想釋放內(nèi)存要使用swap,這樣:
  • vector<type> v;
  • //.... 這里添加許多元素給v
  • //.... 這里刪除v中的許多元素
  • vector<type>(v).swap(v);
  • //此時(shí)v的容量已經(jīng)盡可能的符合其當(dāng)前包含的元素?cái)?shù)量
  • //對(duì)于string則可能像下面這樣
  • string(s).swap(s);
  • 引用《effective stl》的第十二條:當(dāng)涉及 STL容器和線程安全性時(shí),你可以指望一個(gè) STL庫(kù)允許多個(gè)線程同時(shí)讀一個(gè)容器,以及多個(gè)線程對(duì)不同的容器做寫入操作。你不能指望 STL庫(kù)會(huì)把你從手工同步控制中解脫出來(lái),而且你不能依賴于任何線程支持。必須自己去寫多線程安全措施。
  • 2.2 map是怎么實(shí)現(xiàn)的?查找的復(fù)雜度是多少?能不能邊遍歷邊插入?

  • 紅黑樹和散列

  • O(logn)

  • 不可以,map不像vector,它在對(duì)容器執(zhí)行erase操作后不會(huì)返回后一個(gè)元素的迭代器,所以不能遍歷地往后刪除。

  • 2.3 寫多讀少應(yīng)該用什么容器?

    私以為是鏈表,鏈表的插入操作時(shí)常數(shù)時(shí)間復(fù)雜度,訪問(wèn)操作是O(n),是最適合寫多讀少的容器。

    2.4 vector每次insert或erase之后,以前保存的iterator會(huì)不會(huì)失效?

    理論上會(huì)失效,理論上每次insert或者erase之后,所有的迭代器就重新計(jì)算的,所以都可以看作會(huì)失效,原則上是不能使用過(guò)期的內(nèi)存
    但是vector一般底層是用數(shù)組實(shí)現(xiàn)的,我們仔細(xì)考慮數(shù)組的特性,不難得出另一個(gè)結(jié)論,

  • insert時(shí),假設(shè)insert位置在p,分兩種情況:
    a) 容器還有空余空間,不重新分配內(nèi)存,那么p之前的迭代器都有效,p之后的迭代器都失效
    b) 容器重新分配了內(nèi)存,那么p之后的迭代器都無(wú)效咯

  • erase時(shí),假設(shè)erase位置在p,則p之前的迭代器都有效并且p指向下一個(gè)元素位置(如果之前p在尾巴上,則p指向無(wú)效尾end),p之后的迭代器都無(wú)效

  • 2.5 hash_map和map的區(qū)別在哪里?

    hash_map底層是散列的所以理論上操作的平均復(fù)雜度是常數(shù)時(shí)間,map底層是紅黑樹,理論上平均復(fù)雜度是O(logn),下面是借鑒的網(wǎng)上的總結(jié):
    這里總結(jié)一下,選用map還是hash_map,關(guān)鍵是看關(guān)鍵字查詢操作次數(shù),以及你所需要保證的是查詢總體時(shí)間還是單個(gè)查詢的時(shí)間。如果是要很多次操作,要求其整體效率,那么使用hash_map,平均處理時(shí)間短。如果是少數(shù)次的操作,使用 hash_map可能造成不確定的O(N),那么使用平均處理時(shí)間相對(duì)較慢、單次處理時(shí)間恒定的map,考慮整體穩(wěn)定性應(yīng)該要高于整體效率,因?yàn)榍疤嵩诓僮鞔螖?shù)較少。如果在一次流程中,使用hash_map的少數(shù)操作產(chǎn)生一個(gè)最壞情況O(N),那么hash_map的優(yōu)勢(shì)也因此喪盡了。

    2.6 為何map和set不能像vector一樣有個(gè)reserve函數(shù)來(lái)預(yù)分配數(shù)據(jù)?

    map和set內(nèi)部存儲(chǔ)的已經(jīng)不是元素本身了,而是包含元素的節(jié)點(diǎn)。也就是說(shuō)map內(nèi)部使用的Alloc并不是map聲明的時(shí)候從參數(shù)中傳入的Alloc。例如:
    map<int, int,="" less, Alloc?> intmap;
    這時(shí)候在intmap中使用的allocator并不是Alloc, 而是通過(guò)了轉(zhuǎn)換的Alloc,具體轉(zhuǎn)換的方法時(shí)在內(nèi)部通過(guò)
    Alloc::rebind重新定義了新的節(jié)點(diǎn)分配器,詳細(xì)的實(shí)現(xiàn)參看徹底學(xué)習(xí)STL中的Allocator。
    其實(shí)你就記住一點(diǎn),在map和set里面的分配器已經(jīng)發(fā)生了變化,reserve方法你就不要奢望了。

    2.7 當(dāng)數(shù)據(jù)元素增多時(shí)(10000和20000個(gè)比較),map和set的插入和搜索速度變化如何?

    算一下就知道了,首先你得知道m(xù)ap和set的底層都是紅黑樹,紅黑樹的搜索近似于二分查找,二分查找呢,平均時(shí)間復(fù)雜度是log2n,這里簡(jiǎn)寫成logn,
    狂按計(jì)算器:
    log(10000) = 13.3
    log(20000) = 14.3
    看到了不,理論上平均就多了一次,所以你懂的,插起來(lái)。。。

    2.8 auto_ptr可以做vector的元素呢?為什么?

    不能。因?yàn)镾TL的標(biāo)準(zhǔn)容器規(guī)定它所容納的元素必須是可以拷貝構(gòu)造和可被轉(zhuǎn)移賦值的。而auto_ptr不能,可以用shared_ptr智能指針代替。

    三、關(guān)于迭代器的一些問(wèn)題

    3.1 traits技術(shù)原理及應(yīng)用

    這個(gè)問(wèn)題還真不知咋講,簡(jiǎn)單來(lái)說(shuō),在STL算法中用到迭代器時(shí)(肯定會(huì)用到),會(huì)用到迭代器所指之物的型別,假設(shè)算法中有必要聲明一個(gè)變量,以迭代器所指之物為型別,但是C++只支持sizeof()、并未
    支持typeof()。即使typeid(),也只能獲得型別名稱,不能拿來(lái)聲明變量,所以這里就要用到作為”特性萃取機(jī)“的traits技術(shù),當(dāng)然,要讓traits有效運(yùn)作,每個(gè)迭代器設(shè)計(jì)的時(shí)候的遵守約定,自行以內(nèi)嵌型別定義的方式定義出相應(yīng)型別。
    詳情請(qǐng)看書或者移步Traits技術(shù):類型的if-else-then(STL核心技術(shù)之一)

    四、關(guān)于算法的一些問(wèn)題

    4.1 快排算法的樞軸位置是怎么選擇的?

    三點(diǎn)中值法,取整個(gè)序列的頭、尾、中央三個(gè)位置的元素,以其中值作為樞軸。

    4.2 簡(jiǎn)單說(shuō)一下next_permutation和partition的實(shí)現(xiàn)?

  • next_permutation(下一個(gè)排列)
    首先,從最尾端開始往前尋找兩個(gè)相鄰元素,另第一個(gè)元素為i,第二個(gè)元素為ii,且滿足i<ii。找到這樣一組相鄰元素后,再?gòu)奈捕碎_始往前檢驗(yàn),找出第一個(gè)大于i的元素j,將i,j元素對(duì)調(diào),再將ii之后的所有元素顛倒排列。此即所求“下一個(gè)”排列組合。

  • partition
    令頭端迭代器first向尾部移動(dòng),尾部迭代器last向頭部移動(dòng)。當(dāng)first所指的值大于或等于樞軸時(shí)就停下來(lái),當(dāng)last所指的值小于或等于樞軸時(shí)也停下來(lái),然后檢驗(yàn)兩個(gè)迭代器是否交錯(cuò)。如果first仍然在last左邊,就將連著元素互換,然后各自調(diào)整一個(gè)位置(向中央逼近),再繼續(xù)進(jìn)行相同的行為。如果發(fā)現(xiàn)兩個(gè)迭代器叫錯(cuò)了,表示整個(gè)序列已經(jīng)調(diào)整完畢。

  • 五、關(guān)于內(nèi)存配置的一些問(wèn)題

    5.1 stl對(duì)于小內(nèi)存塊請(qǐng)求與釋放的處理

    STL考慮到小型內(nèi)存區(qū)塊的碎片問(wèn)題,設(shè)計(jì)了雙層級(jí)配置器,第一級(jí)配置直接使用malloc()和free();第二級(jí)配置器則視情況采用不同的策略,當(dāng)配置區(qū)大于128bytes時(shí),直接調(diào)用第一級(jí)配置器;當(dāng)配置區(qū)塊小于128bytes時(shí),便不借助第一級(jí)配置器,而使用一個(gè)memory pool來(lái)實(shí)現(xiàn)。究竟是使用第一級(jí)配置器還是第二級(jí)配置器,由一個(gè)宏定義來(lái)控制。SGI STL中默認(rèn)使用第二級(jí)配置器。
    二級(jí)配置器會(huì)將任何小額區(qū)塊的內(nèi)存需求量上調(diào)至8的倍數(shù),(例如需求是30bytes,則自動(dòng)調(diào)整為32bytes),并且在它內(nèi)部會(huì)維護(hù)16個(gè)free-list, 各自管理大小分別為8, 16, 24,…,128bytes的小額區(qū)塊,這樣當(dāng)有小額內(nèi)存配置需求時(shí),直接從對(duì)應(yīng)的free list中拔出對(duì)應(yīng)大小的內(nèi)存(8的倍數(shù));當(dāng)客戶端歸還內(nèi)存時(shí),將根據(jù)歸還內(nèi)存塊的大小,將需要?dú)w還的內(nèi)存插入到對(duì)應(yīng)free list的最頂端。
    小結(jié):
    STL中的內(nèi)存分配器實(shí)際上是基于空閑列表(free list)的分配策略,最主要的特點(diǎn)是通過(guò)組織16個(gè)空閑列表,對(duì)小對(duì)象的分配做了優(yōu)化。
    1)小對(duì)象的快速分配和釋放。當(dāng)一次性預(yù)先分配好一塊固定大小的內(nèi)存池后,對(duì)小于128字節(jié)的小塊內(nèi)存分配和釋放的操作只是一些基本的指針操作,相比于直接調(diào)用malloc/free,開銷小。
    2)避免內(nèi)存碎片的產(chǎn)生。零亂的內(nèi)存碎片不僅會(huì)浪費(fèi)內(nèi)存空間,而且會(huì)給OS的內(nèi)存管理造成壓力。
    3)盡可能最大化內(nèi)存的利用率。當(dāng)內(nèi)存池尚有的空閑區(qū)域不足以分配所需的大小時(shí),分配算法會(huì)將其鏈入到對(duì)應(yīng)的空閑列表中,然后會(huì)嘗試從空閑列表中尋找是否有合適大小的區(qū)域,
    但是,這種內(nèi)存分配器局限于STL容器中使用,并不適合一個(gè)通用的內(nèi)存分配。因?yàn)樗笤卺尫乓粋€(gè)內(nèi)存塊時(shí),必須提供這個(gè)內(nèi)存塊的大小,以便確定回收到哪個(gè)free list中,而STL容器是知道它所需分配的對(duì)象大小的,比如上述:
    stl::vector?array;
    array是知道它需要分配的對(duì)象大小為sizeof(int)。一個(gè)通用的內(nèi)存分配器是不需要知道待釋放內(nèi)存的大小的,類似于free(p)。
    詳情請(qǐng)看書或移步STL源碼剖析---空間配置器

    六、關(guān)于線程安全的一些問(wèn)題

    總結(jié)

    以上是生活随笔為你收集整理的《STL源码剖析》相关面试题总结的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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