前言
deque 双端队列,支持从两头push和pop数据,比一般的队列功能更加强大。也是一个常用的数据结构。本系列博客将来分析deque的实现源码。这个deque比之前看的vector复杂了一些。
基础函数
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));
}
deque的实现比较有意思,它使用一些些内存buf来让队列能够动态的生长。
里面的buf其实就是下面说的一个节点node所指向的东西。这个buf含有若干个元素,那么一个buf到底含有多少个元素嘞?
上面的函数就告诉我们答案了。
如果每个元素的大小 sz 小于 512个字节,那么 一个buf放512 /sz 个元素,否则一个buf就放一个元素。举个例子 ,假设这个一个int的双端队列,那么 每个buf占512字节,每个buf可以放128个int.
同时 这说明了什么呢? 说明deque在给每个buf申请内存的时候最少也会申请512个字节的。
它的内部结构是酱紫的:
那个T数组就是上面这个函数所说的Buf.
迭代器管理类
deque首先使用了一个专门的迭代器类__deque_iterator 来方便自己内部的实现和内存的管理。其完整代码结构如下:
注意这是个struct,因此默认的访问控制关键字是public
#ifndef __STL_NON_TYPE_TMPL_PARAM_BUG
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;
static size_t buffer_size() {return __deque_buf_size(BufSiz, sizeof(T)); }
#else /* __STL_NON_TYPE_TMPL_PARAM_BUG */
template <class T, class Ref, class Ptr>
struct __deque_iterator {
typedef __deque_iterator<T, T&, T*> iterator;
typedef __deque_iterator<T, const T&, const T*> const_iterator;
static size_t buffer_size() {return __deque_buf_size(0, sizeof(T)); }
#endif
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;
T* cur;
T* first;
T* last;
map_pointer node;
__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) {}
reference operator*() const { return *cur; }
#ifndef __SGI_STL_NO_ARROW_OPERATOR
pointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */
difference_type operator-(const self& x) const {
return buffer_size() * (node - x.node - 1) +
(cur - first) + (x.last - x.cur);
}
self& operator++() {
++cur;
if (cur == last) {
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;
}
self& operator+=(difference_type n) {
difference_type offset = n + (cur - first);
if (offset >= 0 && offset < buffer_size())
cur += n;
else {
difference_type node_offset =
offset > 0 ? offset / buffer_size()
: -difference_type((-offset - 1) / buffer_size()) - 1;
set_node(node + node_offset);
cur = first + (offset - node_offset * 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);
}
void set_node(map_pointer new_node) {
node = new_node;
first = *new_node;
last = first + buffer_size();
}
};
我们一部分一部分的分析:
#ifndef __STL_NON_TYPE_TMPL_PARAM_BUG
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;
static size_t buffer_size() {return __deque_buf_size(BufSiz, sizeof(T)); }
#else /* __STL_NON_TYPE_TMPL_PARAM_BUG */
template <class T, class Ref, class Ptr>
struct __deque_iterator {
typedef __deque_iterator<T, T&, T*> iterator;
typedef __deque_iterator<T, const T&, const T*> const_iterator;
static size_t buffer_size() {return __deque_buf_size(0, sizeof(T)); }
#endif
首先 这一部分 根据__STL_NON_TYPE_TMPL_PARAM_BUG 这个宏来条件编译出两个不同的模板,主要区别是最后一个参数 size_t BufSize是否存在。为什么会有这个条件编译呢?
主要是因为 早期有些编译器不支持最后一个参数为int 这样子具体某个值的模板。
举个例子:
template< int i > f();
那 f< 1 >();
f< 2 >();
是不是由template生成的不同模板呢?不同编译器处理不同,但是现在一般都会认为他们不同
在这一段代码中,在__deque_iterator内部定义了两个字段:iterator和const_iterator。这两个迭代器都是接受三个类型参数的
typedef __deque_iterator<T, T&, T*> iterator;
typedef __deque_iterator<T, const T&, const T*> const_iterator;
接下来是这个__deque_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;
T* cur;
T* first;
T* last;
map_pointer node;
node是一个T类型的二重指针
cur,first,last是一个T类型的指针
(未完待续o( ̄︶ ̄)o)
(2017/09/24继续)
上午考完托福,下午继续看源码,爽歪歪 O(∩_∩)O
上面还是在讲__deque_iterator这个工具类,其中要注意的就是四个成员变量:first,cur,last和node.
__deque_iterator管理的是这么一个结构:
node就是图中的map指针,它指向一个T*数组.而这个数组的每一个元素都分别指向不同的T数组,这种T数组又被称一个Buf。first指针指向T数组的开始,last指向末尾,而cur则指向中间的一些节点。
接下来是一些构造函数:
__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) {}
很正常的代码。
接下来 就是重载一些运算符了。
1. 重载了取值运算符*和->.他们都是返回cur的内容或cur指针内容。
reference operator*() const { return *cur; }
#ifndef __SGI_STL_NO_ARROW_OPERATOR
pointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */
2 .设置节点:
void set_node(map_pointer new_node)
{
node = new_node;
first = *new_node;
last = first + buffer_size();
}
这个函数的作用是让node去指向一个新的T*,而这个node新所指的就是一个Buf结构,其实也是一个数组。让first指向Buffer的开始,last指向Buf的最后,注意这个Buf的大小就是size_t __deque_buf_size(size_t n, size_t sz)
函数的返回值,这个函数的解释在上面第二部分有提到。
3. 重载”-“:
difference_type operator-(const self& x) const {
return buffer_size() * (node - x.node - 1) +
(cur - first) + (x.last - x.cur);
}
这个是什么意思的嘞?
如下面这个图所示:
就是从*this到x之间 一共有多少个T元素的意思!
···其它一些函数 需要结合deque来看了
未完待续((^▽^))