SGISTL原始碼閱讀十四 deque容器上
阿新 • • 發佈:2018-11-14
SGISTL原始碼閱讀十四 deque容器上
deque
概述
之前我們學習過vector
,我們知道他的擴容方式是從尾部擴容,也就是它是單向開口的。deque
比vector
和list
都要更復雜一些,它是雙向開口的,也就是說可以在頭部和尾部做同樣的操作(我們要向vector
的頭部新增一個元素代價是相當大的,需要將所有元素向後移動,但是對於deque
來說,在頭部和尾部操作都是同樣的時間複雜度)。這樣看起來deque
是一段連續線性空間,但是它只是邏輯上的一整段連續線性空間,內部實現是由一小段一小段的定量連續空間構成的。下面我們來深入瞭解它。
深入原始碼
deque
的資料結構
map(deque
的中控器)
這裡的map
和容器map
不一樣,它是deque
的中控器。前面提到deque
是由一小段一小段的定量連續空間構成的,將這些小段的定量連續空間整合在一起,就是它的作用了。它用到了一個二級指標。
//預設使用SGISTL的空間配置器 template <class T, class Alloc = alloc, size_t BufSiz = 0> class deque { public: typedef T value_type; //當前型別的指標 別名為pointer typedef value_type* pointer; typedef const value_type* const_pointer; //... protected: //二級指標 typedef pointer* map_pointer; typedef simple_alloc<pointer, Alloc> map_allocator; //... //map初始長度為8 static size_type initial_map_size() { return 8; } //... protected: //它的內部維護了兩個迭代器,分別指向了deque的第一個節點和最後一個節點 iterator start; iterator finish; map_pointer map; size_type map_size; //map內的指標個數
一些基本的操作
public: //返回第一個節點 iterator begin() { return start; } //返回末尾節點 iterator end() { return finish; } const_iterator begin() const { return start; } const_iterator end() const { return finish; } //過載操作符(呼叫__deque_iterator的過載操作符[]),實現下標訪問 reference operator[](size_type n) { return start[difference_type(n)]; } const_reference operator[](size_type n) const { return start[difference_type(n)]; } //返回第一個節點的值 reference front() { return *start; } //返回末尾值,由於[start,finish) reference back() { iterator tmp = finish; --tmp; return *tmp; } const_reference front() const { return *start; } const_reference back() const { const_iterator tmp = finish; --tmp; return *tmp; }
我們通過圖片來加深對deque
的理解
巨集觀
微觀
deque
的迭代器
deque
的迭代器型別是random_access_iterator_tag
,但是它並不是簡單的普通指標。
//全域性函式
//如果n不為0,則直接返回n
//如果n為0,sz < 512bytes,則返回512 / sz
//如果n為0,sz > 512bytes,則返回size_t(1)
inline size_t __deque_buf_size(size_t n, size_t sz)
{
return n != 0 ? n : (sz < 512 ? size_t(512 / sz) : size_t(1));
}
//...
public:
#ifndef __STL_NON_TYPE_TMPL_PARAM_BUG
typedef __deque_iterator<T, T&, T*, BufSiz> iterator;
typedef __deque_iterator<T, const T&, const T&, BufSiz> const_iterator;
#else /* __STL_NON_TYPE_TMPL_PARAM_BUG */
typedef __deque_iterator<T, T&, T*> iterator;
typedef __deque_iterator<T, const T&, const T*> const_iterator;
#endif /* __STL_NON_TYPE_TMPL_PARAM_BUG */
//deque迭代器
template <class T, class Ref, class Ptr, size_t BufSiz>
struct __deque_iterator {
typedef __deque_iterator<T, T&, T*, BufSiz> iterator;
typedef __deque_iterator<T, const T&, const T*, BufSiz> const_iterator;
//呼叫了全域性函式__deque_buf_size
static size_t buffer_size() {return __deque_buf_size(BufSiz, sizeof(T)); }
//沒有繼承std::iterator,所以必須撰寫五個迭代器的相應型別
typedef random_access_iterator_tag iterator_category;
typedef T value_type;
typedef Ptr pointer;
typedef Ref reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef T** map_pointer;
typedef __deque_iterator self;
//維護了3個指標
T* cur; //當前迭代器所指緩衝區中的當前(current)元素
T* first; //當前迭代器所指緩衝區的頭
T* last; //當前得帶其所指緩衝區的尾
map_pointer node; //指向中控器
//deque迭代器的建構函式
__deque_iterator(T* x, map_pointer y)
: cur(x), first(*y), last(*y + buffer_size()), node(y) {}
__deque_iterator() : cur(0), first(0), last(0), node(0) {}
__deque_iterator(const iterator& x)
: cur(x.cur), first(x.first), last(x.last), node(x.node) {}
set_node()
這個函式相當的重要。我們能呼叫它跳出當前線性空間(緩衝區)
void set_node(map_pointer new_node) {
node = new_node;
first = *new_node;
last = first + difference_type(buffer_size());
}
操作符過載
//過載*,返回cur指向元素的值
reference operator*() const { return *cur; }
#ifndef __SGI_STL_NO_ARROW_OPERATOR
//過載->,返回cur指向元素的值
pointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */
/* 過載操作符-
* 計算兩個迭代器指向的元素之間的個數
* (node - x.node - 1)計算的是兩個迭代器所在的線性空間中間跨過的完整的線性空間個數
*/
difference_type operator-(const self& x) const {
return difference_type(buffer_size()) * (node - x.node - 1) +
(cur - first) + (x.last - x.cur);
}
//過載後++
self& operator++() {
++cur;
////如果+1後指向last,說明需要到下一段線性空間(緩衝區)中去取值
if (cur == last) {
//呼叫set_node函式找到下一段線性空間
set_node(node + 1);
cur = first;
}
return *this;
}
//過載前++
self operator++(int) {
self tmp = *this;
++*this;
return tmp;
}
//過載後--
self& operator--() {
if (cur == first) {
set_node(node - 1);
cur = last;
}
--cur;
return *this;
}
//過載前--
self operator--(int) {
self tmp = *this;
--*this;
return tmp;
}
//過載+=
//如果+n後的值不在當前線性空間(緩衝區),仍然需要set_node到相應線性空間(緩衝區)去
self& operator+=(difference_type n) {
difference_type offset = n + (cur - first);
if (offset >= 0 && offset < difference_type(buffer_size()))
cur += n;
else {
difference_type node_offset =
offset > 0 ? offset / difference_type(buffer_size())
: -difference_type((-offset - 1) / buffer_size()) - 1;
set_node(node + node_offset);
cur = first + (offset - node_offset * difference_type(buffer_size()));
}
return *this;
}
self operator+(difference_type n) const {
self tmp = *this;
//呼叫+=
return tmp += n;
}
//類似於+=
self& operator-=(difference_type n) { return *this += -n; }
self operator-(difference_type n) const {
self tmp = *this;
//呼叫-=
return tmp -= n;
}
//過載[],可用下標訪問,實現了隨機訪問
reference operator[](difference_type n) const { return *(*this + n); }
//判斷迭代器所指向的是否為同一段線性空間(緩衝區)的同一個值
bool operator==(const self& x) const { return cur == x.cur; }
bool operator!=(const self& x) const { return !(*this == x); }
bool operator<(const self& x) const {
return (node == x.node) ? (cur < x.cur) : (node < x.node);
}
在學習了deque
的迭代器之後,我們再用一個圖來深入理解deque
的結構體以及它的迭代器
總結
我們主要介紹了deque
的資料結構以及它的迭代器。它確實是一種相對來說比較複雜的容器,但是仔細閱讀原始碼並結合圖示也並不難理解。
因為我們本節並沒有介紹到關於節點node
的概念,其實他由就是map
所維護的。
之後我們將繼續介紹deque
的構造以及記憶體管理等。