概述
vector是單向開口的連續(xù)空間,deque則是雙向開口的連續(xù)空間,可以在頭尾兩端分別做元素的插入和刪除。
deque與vector最大的差異在于:
- deque允許在常數(shù)時(shí)間內(nèi)對(duì)起頭端進(jìn)行元素的插入或者移除。
- deque沒有容量的概念,因?yàn)樗莿?dòng)態(tài)地以分段連續(xù)空間組合而成,隨時(shí)可以增加一段新的空間并鏈接起來。亦既是在vector中那樣"因?yàn)榕f空間不足而重新分配空間,然后復(fù)制元素,再釋放舊空間"這樣的事情在deque中不會(huì)發(fā)生,因此沒有必要保留reserve等容量相關(guān)。
deque由2部分組成:
- 緩沖區(qū):一段連續(xù)的內(nèi)存空間。
- map:指向緩沖區(qū)的指針數(shù)組。
deque.png
template <typename T>
class Deque {
......
private:
T **_map; // 動(dòng)態(tài)數(shù)組
iterator _begin;
iterator _end;
size_t _mapSize = 1; // map的數(shù)量
size_t _pageSize = 3; // 緩沖區(qū)的大小
}
迭代器
deque是分段的連續(xù)空間,維持其"整體連續(xù)"假象的任務(wù),就落在了迭代器身上。為此迭代器必須能夠做到:
- 能夠指向緩沖區(qū)的元素
- 能夠判斷自己是否已處于緩沖區(qū)的邊緣。當(dāng)自己處于緩沖區(qū)的邊緣時(shí),能夠跳躍至上一個(gè)緩沖區(qū)或者下一個(gè)緩沖區(qū),需要知道自己在map中哪個(gè)位置。
所以,在其中應(yīng)該要保留一個(gè)指向容器(其中有map)的指針,一個(gè)所處map為重的索引, 指向元素的指針。
class DequeIterator {
private:
Deque<T> *_containerPtr; // 保存對(duì)容器的連接,重點(diǎn)是能訪問到map。
T *_cur; // 指向緩沖區(qū)的元素
size_t _mapIndex; // map數(shù)組的索引,向前跳躍或者向后跳躍
};
迭代器.png
DequeIterator &operator++();
_cur = isPageTail() ? _containerPtr->_map[++_mapIndex] : ++_cur; // 判斷是否是緩沖區(qū)末尾
return *this;
DequeIterator &operator--()
_cur = isPageHead() ? _containerPtr->_map[--_mapIndex] + (getPageSize()-1) : --_cur; // 判斷是否是緩沖區(qū)頭部
return *this;
添加元素
void push_back(const T &value)
- 如果沒有空間容納新元素
- 申請(qǐng)新的map
- 拷貝原deque的緩沖區(qū)中的元素
- 構(gòu)造元素
if (isLastPageTail()) // 是否是Deque緩沖區(qū)的最后一個(gè)位置
reallocateMapForTail();
dataAlloc::construct(_end._cur, value);
++_end;
void push_front(const T& value)
if (isFirstPageHead())
reallocateMapForHead();
--_begin;
dataAlloc::construct(_begin._cur, value);
刪除元素
void Deque<T>::pop_back()
if (empty())
throw std::out_of_range("pop_back() on empty Deque");
--_end;
dataAlloc::destroy(_end._cur);
void pop_front()
if (empty())
throw std::out_of_range("pop_front() on empty Deque");
dataAlloc::destroy(_begin._cur);
++_begin;