SGI STL源码剖析——deque
deque相对vector和list,平时使用的比较少,但是以deque为基础配接得到的队列queue和栈stack则是经常使用,deque的实现比起vector和list要稍显复杂些。deque使用了分段连续的结构,而为了维护分段连续,其迭代器经过了精心设计。相比vector,deque支持高效率的头部插入,并且deque没有容量的概念,不像vector一旦容量不足就需要重新配置空间。
deque迭代器
欲学习容器,必先学习其迭代器,deque的迭代器经过精心设计,十分值得学习。我们需要先知道deque使用的空间结构可以简单理解成一个二级缓存结构,首先第一级指针指向一个缓存区,而这个缓存区每一个元素呢又是一个指针,指向另外一个缓冲区,这个二级的缓冲区才会真正存放数据元素。二级缓冲区就是一个连续空间,那显然一级缓冲区每个指针指向的空间就是分段了,一级缓冲区本身也是连续的。把一级缓冲区称为map,二级缓冲区称为缓冲区
迭代器其实在运算时改变的是二级缓冲器指向的位置,相当于vector的T *或者元素指针,迭代器指向的位置也同样是二级缓冲区的位置,也就是元素位置,只不过运算时要注意横跨map节点。
inline size_t __deque_buf_size(size_t __size) {
return __size < 512 ? size_t(512 / __size) : size_t(1);
}
template <class _Tp, class _Ref, class _Ptr>
struct _Deque_iterator {
typedef _Deque_iterator<_Tp, _Tp&, _Tp*> iterator;
typedef _Deque_iterator<_Tp, const _Tp&, const _Tp*> const_iterator;
// 决定缓冲区大小
static size_t _S_buffer_size() { return __deque_buf_size(sizeof(_Tp)); }
// 未继承std::iterator,所以自行撰写五个必要的迭代器相应类别
typedef random_access_iterator_tag iterator_category;
typedef _Tp value_type;
typedef _Ptr pointer;
typedef _Ref reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef _Tp** _Map_pointer; // 二级指针
typedef _Deque_iterator _Self;
_Tp* _M_cur; //迭代器当前指向的缓冲区元素
_Tp* _M_first; //迭代器当前指向的缓冲区头
_Tp* _M_last; //迭代器当前指向的缓冲区尾
_Map_pointer _M_node; // 指向map,map是连续空间,每个元素都是指针指向一块缓冲区
//以下是构造函数
_Deque_iterator(_Tp* __x, _Map_pointer __y)
: _M_cur(__x), _M_first(*__y)
,_M_last(*__y + _S_buffer_size())
, _M_node(__y)
{
}
_Deque_iterator()
: _M_cur(0)
, _M_first(0)
, _M_last(0)
, _M_node(0)
{
}
_Deque_iterator(const iterator& __x)
: _M_cur(__x._M_cur), _M_first(__x._M_first)
, _M_last(__x._M_last), _M_node(__x._M_node)
{
}
// 重载*运算符,返回迭代器当前指向的缓冲区元素
reference operator*() const { return *_M_cur; }
#ifndef __SGI_STL_NO_ARROW_OPERATOR
pointer operator->() const { return _M_cur; }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */
// 重载运算符,两个迭代器相减
difference_type operator-(const _Self& __x) const {
return difference_type(_S_buffer_size()) * (_M_node - __x._M_node - 1) +
(_M_cur - _M_first) + (__x._M_last - __x._M_cur);
}
//重载前置++运算符
_Self& operator++() {
++_M_cur; // 切换至下一元素
if (_M_cur == _M_last) { // 到达缓冲区的尾端
_M_set_node(_M_node + 1); // map指针+1,切换至下一缓冲区的第一个元素
_M_cur = _M_first;
}
return *this;
}
//重载后置++运算符
_Self operator++(int) {
_Self __tmp = *this;
++*this;
return __tmp;
}
//注意--和++的区别,++是先移动然后判断是否到尾,--则是先判断是否到头然后再移动
_Self& operator--() {
if (_M_cur == _M_first) {
_M_set_node(_M_node - 1);
_M_cur = _M_last;
}
--_M_cur;
return *this;
}
_Self operator--(int) {
_Self __tmp = *this;
--*this;
return __tmp;
}
//random迭代器支持算术运算
_Self& operator+=(difference_type __n)
{
// 判断目标是否在同一缓冲区
difference_type __offset = __n + (_M_cur - _M_first);
if (__offset >= 0 && __offset < difference_type(_S_buffer_size()))
//未超出一个缓冲区
_M_cur += __n;
else {
//超出一个缓冲区,计算需要移动几个缓冲区,移动距离分为正负两种总情况
difference_type __node_offset =
__offset > 0 ? __offset / difference_type(_S_buffer_size())
: -difference_type((-__offset - 1) / _S_buffer_size()) - 1;
_M_set_node(_M_node + __node_offset);
_M_cur = _M_first +
(__offset - __node_offset * difference_type(_S_buffer_size()));
}
return *this;
}
// 以下几个重载都是直接调用operator+=
_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;
}
// 调用operator* operator+完成随机访问
// 通过前面重载的运算符就能正确找到对应元素
reference operator[](difference_type __n) const { return *(*this + __n); }
// 从中可以充分学习运算符重载的技巧
bool operator==(const _Self& __x) const { return _M_cur == __x._M_cur; }
bool operator!=(const _Self& __x) const { return !(*this == __x); }
bool operator<(const _Self& __x) const {
//比较位置
return (_M_node == __x._M_node) ? (_M_cur < __x._M_cur) : (_M_node < __x._M_node);
}
bool operator>(const _Self& __x) const { return __x < *this; }
bool operator<=(const _Self& __x) const { return !(__x < *this); }
bool operator>=(const _Self& __x) const { return !(*this < __x); }
// 切换缓冲区,map本身是连续的,每一个元素是指针
void _M_set_node(_Map_pointer __new_node) {
_M_node = __new_node;
_M_first = *__new_node;
_M_last = _M_first + difference_type(_S_buffer_size());
}
};
deque定义
遵循同样的规则,deque首先定义了一个基类,基类负责完成空间配置
template <class _Tp, class _Alloc>
class _Deque_base {
public:
// 迭代器
typedef _Deque_iterator<_Tp,_Tp&,_Tp*> iterator;
typedef _Deque_iterator<_Tp,const _Tp&,const _Tp*> const_iterator;
typedef _Alloc allocator_type;
allocator_type get_allocator() const { return allocator_type(); }
// 基类构造函数, 指定元素个数
_Deque_base(const allocator_type&, size_t __num_elements)
: _M_map(0), _M_map_size(0), _M_start(), _M_finish() {
// 初始化map
_M_initialize_map(__num_elements);
}
_Deque_base(const allocator_type&)
: _M_map(0), _M_map_size(0), _M_start(), _M_finish() {}
// 析构
~_Deque_base();
protected:
// 初始化map
void _M_initialize_map(size_t __num_elements)
{
// 需要节点数=(元素个数/每个缓冲区可容纳的元素个数)+1
size_t __num_nodes = __num_elements / __deque_buf_size(sizeof(_Tp)) + 1;
// map要管理几个节点,最少 8个,或者是 “所需节点数加 2”
_M_map_size = max((size_t) _S_initial_map_size, __num_nodes + 2);
// 配置出map空间,反会map首地址
_M_map = _M_allocate_map(_M_map_size);
// 这个地方没看明白有什么意义
_Tp** __nstart = _M_map + (_M_map_size - __num_nodes) / 2;
_Tp** __nfinish = __nstart + __num_nodes;
_M_create_nodes(__nstart, __nfinish);
// 设置迭代器,start迭代器指向map中start节点对应缓冲区首
_M_start._M_set_node(__nstart);
_M_finish._M_set_node(__nfinish - 1);
_M_start._M_cur = _M_start._M_first;
// 元素个数/每个缓冲区可容纳的元素个数 的余数,就是最后一个缓冲区有多少个元素
_M_finish._M_cur = _M_finish._M_first + __num_elements % __deque_buf_size(sizeof(_Tp));
}
// 根据map创建对应缓冲区
void _M_create_nodes(_Tp** __nstart, _Tp** __nfinish)
{
_Tp** __cur;
for (__cur = __nstart; __cur < __nfinish; ++__cur)
// 注意这是一个二级指针,一次为每一个节点分配空间,之后节点指向分配的空间
*__cur = _M_allocate_node();
}
void _M_destroy_nodes(_Tp** __nstart, _Tp** __nfinish)
{
for (_Tp** __n = __nstart; __n < __nfinish; ++__n)
_M_deallocate_node(*__n);
}
enum { _S_initial_map_size = 8 };
protected:
_Tp** _M_map;
size_t _M_map_size;
iterator _M_start;
iterator _M_finish;
// 分配map和分派缓冲区两个空间配置器,node对应缓冲区
typedef simple_alloc<_Tp, _Alloc> _Node_alloc_type;
typedef simple_alloc<_Tp*, _Alloc> _Map_alloc_type;
// 分配缓冲区空间
_Tp* _M_allocate_node()
{ return _Node_alloc_type::allocate(__deque_buf_size(sizeof(_Tp))); }
// 回收缓冲区空间
void _M_deallocate_node(_Tp* __p)
{ _Node_alloc_type::deallocate(__p, __deque_buf_size(sizeof(_Tp))); }
// 分配map空间
_Tp** _M_allocate_map(size_t __n)
{ return _Map_alloc_type::allocate(__n); }
// 回收map空间
void _M_deallocate_map(_Tp** __p, size_t __n)
{ _Map_alloc_type::deallocate(__p, __n); }
};
deque的定义继承这个基类,
template <class _Tp, class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) >
class deque : protected _Deque_base<_Tp, _Alloc> {
// 基类名
typedef _Deque_base<_Tp, _Alloc> _Base;
public: // Basic types
typedef _Tp value_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef typename _Base::allocator_type allocator_type;
allocator_type get_allocator() const { return _Base::get_allocator(); }
public:
// 迭代器
typedef typename _Base::iterator iterator;
typedef typename _Base::const_iterator const_iterator;
protected:
// 这里就是Tp**,就是map节点元素
static size_t _S_buffer_size() { return __deque_buf_size(sizeof(_Tp)); }
deque初始化和构造
先来看一下deque支持的构造函数,
// 无参构造
explicit deque(const allocator_type& __a = allocator_type())
: _Base(__a, 0) {}
// 复制构造函数 deque<int> test
deque(const deque& __x) : _Base(__x.get_allocator(), __x.size())
{ uninitialized_copy(__x.begin(), __x.end(), _M_start); }
// 元素数量n,初始值value,先基类构造分配空间,然后填充初始值 deque<int> test(10, 1)
deque(size_type __n, const value_type& __value,
const allocator_type& __a = allocator_type()) : _Base(__a, __n)
{ _M_fill_initialize(__value); }
// 指定元素数量n构造 deque<int> test(10)
explicit deque(size_type __n) : _Base(allocator_type(), __n)
{ _M_fill_initialize(value_type()); }
// 接受迭代器的版本
template <class _InputIterator>
deque(_InputIterator __first, _InputIterator __last,
const allocator_type& __a = allocator_type()) : _Base(__a) {
typedef typename _Is_integer<_InputIterator>::_Integral _Integral;
_M_initialize_dispatch(__first, __last, _Integral());
}
// 迭代器
template <class _InputIter>
void _M_initialize_dispatch(_InputIter __first, _InputIter __last,
__false_type) {
_M_range_initialize(__first, __last, __ITERATOR_CATEGORY(__first));
}
deque的头插和尾插
deque最大特点是支持头部个尾部插入,这当中涉及到其内存空间管理,特别是头尾两个缓冲区对应迭代器的操作略微有些不同,比如尾部缓冲区的cur指向的是下一个节点,那么构造新元素直接在这个cur指向的空间上构造然后移动cur,而头部缓冲区的cur指向的是头部第一个元素,构造新元素必须新移动cur再构造。先来看尾部插入push_back
void push_back(const value_type& __t) {
//尾部至少要有两个元素
if (_M_finish._M_cur != _M_finish._M_last - 1) {
construct(_M_finish._M_cur, __t);
++_M_finish._M_cur;
}
//否则就需要重新配置空间
else
_M_push_back_aux(__t);
}
void _M_push_back_aux(const value_type& __t)
{
value_type __t_copy = __t;
//map本身是一段连续空间,所以会存在空间不足,那就需要重新配置、拷贝数据、销毁原空间
_M_reserve_map_at_back();
*(_M_finish._M_node + 1) = _M_allocate_node();
//前面判断是原来的尾部缓冲区可以再放一个元素才会满
construct(_M_finish._M_cur, __t_copy);
//因为最后一个缓冲区已经放满了,所以finsh要+1
_M_finish._M_set_node(_M_finish._M_node + 1);
_M_finish._M_cur = _M_finish._M_first;
}
void _M_reserve_map_at_back (size_type __nodes_to_add = 1) {
if (__nodes_to_add + 1 > _M_map_size - (_M_finish._M_node - _M_map))
_M_reallocate_map(__nodes_to_add, false);
}
尾插和头部插入是类似的,
void push_front(const value_type& __t) {
//注意这里,start节点对应的缓冲区可以看成为头插设计,从后往前插入元素
//但是注意,末尾缓冲区的cur是已经指向了下一个元素位置,而开始的节点的cur则需要手动前移一下再构造
if (_M_start._M_cur != _M_start._M_first) {
construct(_M_start._M_cur - 1, __t);
--_M_start._M_cur;
}
else
_M_push_front_aux(__t);
}
void _M_push_front_aux(const value_type& __t)
{
value_type __t_copy = __t;
//检查map头部是否空间不足
_M_reserve_map_at_front();
*(_M_start._M_node - 1) = _M_allocate_node();
_M_start._M_set_node(_M_start._M_node - 1);
//始终记住前闭后开,所以last不放元素,last-1是最后一个元素,从和往前放
_M_start._M_cur = _M_start._M_last - 1;
//先改变start节点再构造
construct(_M_start._M_cur, __t_copy);
}
既然说到了头插和尾插,那必然要顺便看一下头部删除和尾部删除,尾部删除还是pop_back()
void pop_back() {
// 最后缓冲区有一个或以上元素,尾部缓冲区cur指向的是下一个元素,如果等于first那就说明没有元素
if (_M_finish._M_cur != _M_finish._M_first) {
--_M_finish._M_cur;
destroy(_M_finish._M_cur);
}
// 最后缓冲区没有元素, 释放缓冲区
else
_M_pop_back_aux();
}
void _M_pop_back_aux()
{
_M_deallocate_node(_M_finish._M_first); // 释放最后一个缓冲区
_M_finish._M_set_node(_M_finish._M_node - 1);
_M_finish._M_cur = _M_finish._M_last - 1;
//注意这里删除了最后一个元素,cur就指向了一个空的位置,就是下一个元素
destroy(_M_finish._M_cur);
}
头部删除是pop_front()
void pop_front() {
// 第一个缓冲区有一个或以上元素
if (_M_start._M_cur != _M_start._M_last - 1) {
destroy(_M_start._M_cur);
++_M_start._M_cur;
}
// 仅有一个元素,释放缓冲区
else
_M_pop_front_aux();
}
void _M_pop_front_aux()
{
destroy(_M_start._M_cur); // 析构第一个元素
_M_deallocate_node(_M_start._M_first); // 释放缓冲区
_M_start._M_set_node(_M_start._M_node + 1);
// 注意只有新建一个头部缓冲区cur才会指向尾部,释放完时cur指向first
_M_start._M_cur = _M_start._M_first;
}
deque的元素操作
常用的容器操作,clear、erase和insert
clear时,deque空状态会保留一个缓冲区,这点要注意。
// 注意,最终需要保留㆒个缓冲区。这是 deque的策略,也是 deque的初始状态。
template <class _Tp, class _Alloc>
void deque<_Tp,_Alloc>::clear()
{
// 头尾中间的缓冲区一定是饱和的
for (_Map_pointer __node = _M_start._M_node + 1;
__node < _M_finish._M_node;
++__node) {
// 析构元素
destroy(*__node, *__node + _S_buffer_size());
_M_deallocate_node(*__node); // 释放缓冲区
}
// 头尾两个缓冲区
if (_M_start._M_node != _M_finish._M_node) {
// 析构头缓冲区元素
destroy(_M_start._M_cur, _M_start._M_last);
// 析构尾缓冲区元素
destroy(_M_finish._M_first, _M_finish._M_cur);
// 仅释放尾缓冲区
_M_deallocate_node(_M_finish._M_first);
}
// 只有一个缓冲区,只析构元素
else
destroy(_M_start._M_cur, _M_finish._M_cur);
_M_finish = _M_start;
}
ok,deque就看到这里了,相比vector和list,deque的结构复杂了许多,不过也不算难理解,主要是迭代器和元素的对应关系要理清。