C++ deque/queue/stack的底層原理解析
deque容器的存儲(chǔ)結(jié)構(gòu)
和 vector 容器采用連續(xù)的線性空間不同,deque 容器存儲(chǔ)數(shù)據(jù)的空間是由一段一段等長(zhǎng)的連續(xù)空間構(gòu)成,各段空間之間并不一定是連續(xù)的,可以位于在內(nèi)存的不同區(qū)域。
deque采用一塊所謂的map數(shù)組(注意,不是STL的map容器)作為主控。這里所謂map是一小塊連續(xù)空間(類似于vector),其中每個(gè)元素(此處稱為一個(gè)節(jié)點(diǎn),node)都是指針,指向另一段(較大的)連續(xù)線性空間,稱為緩沖區(qū)。緩沖區(qū)才是deque的儲(chǔ)存空間主體。SGI STL 允許我們指定緩沖區(qū)大小,默認(rèn)值0表示將使用512 bytes 緩沖區(qū)。

通過(guò)建立 map 數(shù)組,deque 容器申請(qǐng)的這些分段的連續(xù)空間就能實(shí)現(xiàn)“整體連續(xù)”的效果。換句話說(shuō),當(dāng) deque 容器需要在頭部或尾部增加存儲(chǔ)空間時(shí),它會(huì)申請(qǐng)一段新的連續(xù)空間,同時(shí)在 map 數(shù)組的開(kāi)頭或結(jié)尾添加指向該空間的指針,由此該空間就串接到了 deque 容器的頭部或尾部。
如果 map 數(shù)組滿了怎么辦?很簡(jiǎn)單,再申請(qǐng)一塊更大的連續(xù)空間供 map 數(shù)組使用,將原有數(shù)據(jù)(很多指針)拷貝到新的 map 數(shù)組中,然后釋放舊的空間。
deque 容器的分段存儲(chǔ)結(jié)構(gòu),提高了在序列兩端添加或刪除元素的效率,但也使該容器迭代器的底層實(shí)現(xiàn)變得更復(fù)雜。
deque容器迭代器的底層實(shí)現(xiàn)
由于 deque 容器底層將序列中的元素分別存儲(chǔ)到了不同段的連續(xù)空間中,因此要想實(shí)現(xiàn)迭代器的功能,必須先解決如下 2 個(gè)問(wèn)題:
- 迭代器在遍歷 deque 容器時(shí),必須能夠確認(rèn)各個(gè)連續(xù)空間在 map 數(shù)組中的位置;
- 迭代器在遍歷某個(gè)具體的連續(xù)空間時(shí),必須能夠判斷自己是否已經(jīng)處于空間的邊緣位置。如果是,則一旦前進(jìn)或者后退,就需要跳躍到上一個(gè)或者下一個(gè)連續(xù)空間中。
為了實(shí)現(xiàn)遍歷 deque 容器的功能,deque 迭代器定義了如下的結(jié)構(gòu):
template<class T,...>
struct __deque_iterator{
...
T* cur;
T* first;
T* last;
map_pointer node;//map_pointer 等價(jià)于 T**
}可以看到,迭代器內(nèi)部包含 4 個(gè)指針,它們各自的作用為:
- cur:指向當(dāng)前正在遍歷的元素;
- first:指向當(dāng)前連續(xù)空間的首地址;
- last:指向當(dāng)前連續(xù)空間的末尾地址;
- node:它是一個(gè)二級(jí)指針,用于指向 map 數(shù)組中存儲(chǔ)的指向當(dāng)前連續(xù)空間的指針。
借助這 4 個(gè)指針,deque 迭代器對(duì)隨機(jī)訪問(wèn)迭代器支持的各種運(yùn)算符進(jìn)行了重載,能夠?qū)?deque 分段連續(xù)空間中存儲(chǔ)的元素進(jìn)行遍歷。例如:
//當(dāng)?shù)魈幱诋?dāng)前連續(xù)空間邊緣的位置時(shí),如果繼續(xù)遍歷,就需要跳躍到其它的連續(xù)空間中,該函數(shù)可用來(lái)實(shí)現(xiàn)此功能
void set_node(map_pointer new_node){
node = new_node;//記錄新的連續(xù)空間在 map 數(shù)組中的位置
first = *new_node; //更新 first 指針
//更新 last 指針,difference_type(buffer_size())表示每段連續(xù)空間的長(zhǎng)度
last = first + difference_type(buffer_size());
}
//重載 * 運(yùn)算符
reference operator*() const{return *cur;}
pointer operator->() const{return &(operator *());}
//重載前置 ++ 運(yùn)算符
self & operator++(){
++cur;
//處理 cur 處于連續(xù)空間邊緣的特殊情況
if(cur == last){
//調(diào)用該函數(shù),將迭代器跳躍到下一個(gè)連續(xù)空間中
set_node(node+1);
//對(duì) cur 重新賦值
cur = first;
}
return *this;
}
//重置前置 -- 運(yùn)算符
self& operator--(){
//如果 cur 位于連續(xù)空間邊緣,則先將迭代器跳躍到前一個(gè)連續(xù)空間中
if(cur == first){
set_node(node-1);
cur == last;
}
--cur;
return *this;
}deque容器的底層實(shí)現(xiàn)
了解了 deque 容器底層存儲(chǔ)序列的結(jié)構(gòu),以及 deque 容器迭代器的內(nèi)部結(jié)構(gòu)之后,接下來(lái)看看 deque 容器究竟是如何實(shí)現(xiàn)的。
deque 容器除了維護(hù)先前講過(guò)的 map 數(shù)組,還需要維護(hù) start、finish 這 2 個(gè) deque 迭代器。以下為 deque 容器的定義:
//_Alloc為內(nèi)存分配器
template<class _Ty,
class _Alloc = allocator<_Ty>>
class deque{
...
protected:
iterator start;
iterator finish;
map_pointer map;
...
}其中,start 迭代器記錄著 map 數(shù)組中首個(gè)連續(xù)空間的信息,finish 迭代器記錄著 map 數(shù)組中最后一個(gè)連續(xù)空間的信息。另外需要注意的是,和普通 deque 迭代器不同,start 迭代器中的 cur 指針指向的是連續(xù)空間中首個(gè)元素;而 finish 迭代器中的 cur 指針指向的是連續(xù)空間最后一個(gè)元素的下一個(gè)位置。
因此,deque 容器的底層實(shí)現(xiàn)如下圖所示:

借助 start 和 finish,以及 deque 迭代器中重載的諸多運(yùn)算符,就可以實(shí)現(xiàn) deque 容器提供的大部分成員函數(shù),比如:
//begin() 成員函數(shù)
iterator begin() {return start;}
//end() 成員函數(shù)
iterator end() { return finish;}
//front() 成員函數(shù)
reference front(){return *start;}
//back() 成員函數(shù)
reference back(){
iterator tmp = finish;
--tmp;
return *tmp;
}
//size() 成員函數(shù)
size_type size() const{return finish - start;}//deque迭代器重載了 - 運(yùn)算符
//enpty() 成員函數(shù)
bool empty() const{return finish == start;}stack和queue的原理
由stack和queue源碼可知,其實(shí)stack和queue是將deque容器進(jìn)行再封裝,其底層是一個(gè)deque容器。對(duì)stack和queue操作,其實(shí)間接操作的是deque容器。
到此這篇關(guān)于C++ deque/queue/stack的底層原理的文章就介紹到這了,更多相關(guān)C++ deque/queue/stack的底層原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++雙線程調(diào)用網(wǎng)絡(luò)攝像頭與多線程調(diào)用多攝像頭同步執(zhí)行方法詳細(xì)講解
這篇文章主要介紹了C++雙線程調(diào)用網(wǎng)絡(luò)攝像頭與多線程調(diào)用多攝像頭同步執(zhí)行方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2022-11-11
C語(yǔ)言 結(jié)構(gòu)體(Struct)詳解及示例代碼
本文主要介紹C語(yǔ)言 結(jié)構(gòu)體的知識(shí),學(xué)習(xí)C語(yǔ)言肯定需要學(xué)習(xí)結(jié)構(gòu)體,這里詳細(xì)說(shuō)明了結(jié)構(gòu)體并附示例代碼,供大家參考學(xué)習(xí),有需要的小伙伴可以參考下2016-08-08
C++實(shí)現(xiàn)LeetCode(146.近最少使用頁(yè)面置換緩存器)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(146.近最少使用頁(yè)面置換緩存器),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07
帶你用C語(yǔ)言實(shí)現(xiàn)strtok和字符串分割函數(shù)
下面小編就為大家?guī)?lái)一篇c語(yǔ)言中字符串分割函數(shù)及實(shí)現(xiàn)方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2021-09-09
C++中事件機(jī)制的簡(jiǎn)潔實(shí)現(xiàn)及需要放棄的特性
事件模型是被廣泛使用的好東西,但是C++標(biāo)準(zhǔn)庫(kù)里沒(méi)有現(xiàn)成的,現(xiàn)在VC11可以用在XP下了,那么就痛快的拿起C++11提供的先進(jìn)設(shè)施組合出一個(gè)輕便的實(shí)現(xiàn)吧感興趣的朋友可以了解下,或許對(duì)你有所幫助2013-02-02
Qt實(shí)現(xiàn)定時(shí)器的兩種方法分享
這篇文章主要為大家詳細(xì)介紹了Qt中實(shí)現(xiàn)定時(shí)器的兩種不同方法,文中的示例代碼講解詳細(xì),對(duì)我們了解Qt有一定的幫助,感興趣的可以跟隨小編一起學(xué)習(xí)一下2022-11-11
盤點(diǎn)分析C語(yǔ)言中少見(jiàn)卻強(qiáng)大的字符串函數(shù)
這篇文章主要為大家盤點(diǎn)及分析C語(yǔ)言中少見(jiàn)卻強(qiáng)大的字符串函數(shù),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-02-02

