deque是一种双向开口的连续性空间,可以在头尾两端分别做元素的插入删除操作,deque没有所谓的容量(capacity)概念,因为它是动态地以分段连续空间组合而成,随时可以增加一段新空间并链接起来。
虽然deque也提供了Random access iterator,但是它的迭代器不是普通指针,其复杂程度很高,同时,对于deque的排序操作,为了高效率,可以先将deque复制到vector中,排序之后,再复制回deque。
// 默认构造函数
explicit deque(const allocator_type& __a = allocator_type())
: _Base(__a, 0) {}
// 拷贝构造函数
deque(const deque& __x) : _Base(__x.get_allocator(), __x.size())
{ uninitialized_copy(__x.begin(), __x.end(), _M_start); }
// 指定初始元素数目
deque(size_type __n, const value_type& __value,
const allocator_type& __a = allocator_type()) : _Base(__a, __n)
{ _M_fill_initialize(__value); }
// 为每一个节点的缓冲区设定初值
deque(const value_type* __first, const value_type* __last,
const allocator_type& __a = allocator_type())
: _Base(__a, __last - __first)
{ uninitialized_copy(__first, __last, _M_start); }
deque(const_iterator __first, const_iterator __last,
const allocator_type& __a = allocator_type())
: _Base(__a, __last - __first)
{ uninitialized_copy(__first, __last, _M_start); }
deque的迭代器数据结构为:
template
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)); }
typedef random_access_iterator_tag iterator_category; // Random access iterator
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控制中心
}
deque主要的数据结构有:
template
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: // Iterators
typedef typename _Base::iterator iterator;
typedef typename _Base::const_iterator const_iterator;
typedef pointer* _Map_pointer;
protected:
_Tp** _M_map; // 指向map,map是一块连续空间,其中每个元素都是指针,指向一块缓冲区
size_t _M_map_size; // map可以容纳的指针数目
iterator _M_start; // 头部迭代器
iterator _M_finish; // 尾部迭代器
}
deque是采用一块所谓的map作为主控,这里的map是一块连续空间,其中每个元素都是指针,指向一块缓冲区,如下图所示:
我们首先以deque的一个构造函数来进行讲解
该构造函数原型为
deque(size_type __n, const value_type& __value,
const allocator_type& __a = allocator_type()) : _Base(__a, __n)
{ _M_fill_initialize(__value); }
该构造函数指定了deque的初始元素数量以及初始值,其内部会先调用基类的初始化操作,然后再通过_M_fill_initialize函数来进行数据填充,基类的构造函数为
_Deque_base(const allocator_type&, size_t __num_elements)
: _M_map(0), _M_map_size(0), _M_start(), _M_finish() {
_M_initialize_map(__num_elements);
}
该构造函数通过_M_initialize_map函数来对map主控器进行初始化,其初始化过程如下:
template
void
_Deque_base<_Tp,_Alloc>::_M_initialize_map(size_t __num_elements)
{
// map所需节点数 = (元素个数/每个缓冲区可容纳的元素个数) + 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);
_M_map = _M_allocate_map(_M_map_size);
// __nstart、__nfinish 指向 map 的中间位置
// 保持头尾两端的扩充容量一样大
_Tp** __nstart = _M_map + (_M_map_size - __num_nodes) / 2;
_Tp** __nfinish = __nstart + __num_nodes;
__STL_TRY {
// 为map内的每个现用节点配置缓冲区
_M_create_nodes(__nstart, __nfinish);
}
__STL_UNWIND((_M_deallocate_map(_M_map, _M_map_size),
_M_map = 0, _M_map_size = 0));
// 为 deque 内的两个迭代器 start 和 finish 指向正确位置
_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));
}
_M_fill_initialize源码如下:
template
void deque<_Tp,_Alloc>::_M_fill_initialize(const value_type& __value) {
_Map_pointer __cur;
__STL_TRY {
// 为每个节点的缓冲区设置初始值
for (__cur = _M_start._M_node; __cur < _M_finish._M_node; ++__cur)
uninitialized_fill(*__cur, *__cur + _S_buffer_size(), __value);
// 最后一个节点有点不同,因为尾端可能会有备用空间,不必设置初值
uninitialized_fill(_M_finish._M_first, _M_finish._M_cur, __value);
}
__STL_UNWIND(destroy(_M_start, iterator(*__cur, __cur)));
}
下面来看添加元素时的代码。
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);
}
_M_push_back_aux代码如下:
template
void deque<_Tp,_Alloc>::_M_push_back_aux()
{
// 若符合某种条件,则必须重换一个map
_M_reserve_map_at_back();
// 配置新节点
*(_M_finish._M_node + 1) = _M_allocate_node();
__STL_TRY {
construct(_M_finish._M_cur); // 设置元素值
_M_finish._M_set_node(_M_finish._M_node + 1); // 改变finish,令其指向新节点
_M_finish._M_cur = _M_finish._M_first; // 设定finish的新状态
}
__STL_UNWIND(_M_deallocate_node(*(_M_finish._M_node + 1)));
}
push_back是向尾部添加元素,下面来看deque先首部添加元素的函数push_front。
void push_front(const value_type& __t) {
// 第一缓冲区尚有备用空间
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);
}
_M_push_front_aux源码为
template
void deque<_Tp,_Alloc>::_M_push_front_aux()
{
// 若符合某种条件,则必须重换一个map
_M_reserve_map_at_front();
// 配置新节点
*(_M_start._M_node - 1) = _M_allocate_node();
__STL_TRY {
_M_start._M_set_node(_M_start._M_node - 1); // 改变start,令其指向新节点
_M_start._M_cur = _M_start._M_last - 1; // 设定start的初始值
construct(_M_start._M_cur); // 针对标的元素设值
}
__STL_UNWIND((++_M_start, _M_deallocate_node(*(_M_start._M_node - 1))));
}
在添加元素时,都会涉及到重换map的操作,那何时重换map呢,其源代码如下:
void _M_reserve_map_at_back (size_type __nodes_to_add = 1) {
// 如果map尾端的节点备用空间不足,则重换map,即配置更大的map,拷贝原来的map,并释放原来的map
if (__nodes_to_add + 1 > _M_map_size - (_M_finish._M_node - _M_map))
_M_reallocate_map(__nodes_to_add, false);
}
void _M_reserve_map_at_front (size_type __nodes_to_add = 1) {
if (__nodes_to_add > size_type(_M_start._M_node - _M_map))
_M_reallocate_map(__nodes_to_add, true);
}
最终操作都是由_M_reallocate_map来完成的,其源码如下
// 重新分配 map 连续空间
template
void deque<_Tp,_Alloc>::_M_reallocate_map(size_type __nodes_to_add,
bool __add_at_front)
{
size_type __old_num_nodes = _M_finish._M_node - _M_start._M_node + 1;
size_type __new_num_nodes = __old_num_nodes + __nodes_to_add;
_Map_pointer __new_nstart;
if (_M_map_size > 2 * __new_num_nodes) {
__new_nstart = _M_map + (_M_map_size - __new_num_nodes) / 2
+ (__add_at_front ? __nodes_to_add : 0);
if (__new_nstart < _M_start._M_node)
copy(_M_start._M_node, _M_finish._M_node + 1, __new_nstart);
else
copy_backward(_M_start._M_node, _M_finish._M_node + 1,
__new_nstart + __old_num_nodes);
}
else {
// 配置一块新空间,准备给新map使用
size_type __new_map_size =
_M_map_size + max(_M_map_size, __nodes_to_add) + 2;
_Map_pointer __new_map = _M_allocate_map(__new_map_size);
__new_nstart = __new_map + (__new_map_size - __new_num_nodes) / 2
+ (__add_at_front ? __nodes_to_add : 0);
// 把原来map的内容拷贝过来
copy(_M_start._M_node, _M_finish._M_node + 1, __new_nstart);
// 释放掉原来的map
_M_deallocate_map(_M_map, _M_map_size);
_M_map = __new_map;
_M_map_size = __new_map_size;
}
// 重新设置迭代器start和finish
_M_start._M_set_node(__new_nstart);
_M_finish._M_set_node(__new_nstart + __old_num_nodes - 1);
}
下面来看取出元素的函数操作。
void pop_back() {
// 最后一个缓冲区有一个或更多元素
if (_M_finish._M_cur != _M_finish._M_first) {
--_M_finish._M_cur;
destroy(_M_finish._M_cur);
}
else // 最后缓冲区没有任何元素
_M_pop_back_aux();
}
_M_pop_back_aux源码为:
template
void deque<_Tp,_Alloc>::_M_pop_back_aux()
{
_M_deallocate_node(_M_finish._M_first); // 释放最后一个缓冲区
_M_finish._M_set_node(_M_finish._M_node - 1); // 调整finish的状态,使其指向前一个缓冲区的最后一个元素
_M_finish._M_cur = _M_finish._M_last - 1;
destroy(_M_finish._M_cur); // 将该元素析构
}
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();
}
_M_pop_front_aux源码为
template
void deque<_Tp,_Alloc>::_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); // 调整start的状态,使其指向后一个缓冲区的第一个元素
_M_start._M_cur = _M_start._M_first;
}
template
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;
}
deque是一种双向开口的连续性空间,可以在头尾两端分别做元素的插入删除操作,deque使用连续的map中控来管理许多不连续的元素缓冲区,虽然deque提供了Random access iterator,但是由于它的特殊结构,导致迭代器的复杂程度较高,源码就不再一一介绍。
侯捷 《STL源码剖析》
SGI-STL V3.3 源代码的学习