前言
deque是一种双向开口的分段连续线性空间,可以在头尾端进行元素的插入和删除。deque与vector最大的差异就是:第一是deque允许于常数时间内对头端进行插入或删除元素;第二是deque是分段连续线性空间,随时可以增加一段新的空间;deque不像vector那样,vector当内存不够时,需重新分配/复制数据/释放原始空间;不过deque的迭代器设置比vector复杂,因为迭代器不能使用普通指针,因此尽量使用vector; 当对deque的元素进行排序时,为了提高效率,首先把deque数据复制到vector,利用vector的排序算法(利用STL的sort算法),排序之后再次复制回deque;本文所列的源码出自SGI STL中的文件<stl_deque.h>,对于deque容器类的详细信息也可以查看《deque容器库》和《MSDN的deque类》
deque容器
deque中控器
由于deque是分段连续线性空间的,为了用户方便操作,所以它对外的接口类似于连续空间。为了管理这些分段空间deque容器引入了一种中控器map,map是一块连续的空间,其中每个元素是指向缓冲区的指针,缓冲区才是deque存储数据的主体。下面给出map的结构;
_Tp** _M_map;
size_t _M_map_size;
deque的迭代器
deque是分段连续的线性空间,则普通指针不能作为deque的迭代器。迭代器设计时必须能够进行
operator++
或
operator--
操作,为了能够是用户随机访问容器,则必须对操作符进行重载。
deque迭代器的功能:
迭代器的数据结构如下:
deque迭代器的功能:
- 必须知道缓冲区的位置
- 能够判断是否处于其所在缓冲区的边界
- 能够知道其所在缓冲区当前所指位置的元素
struct _Deque_iterator {
...
//以下是迭代器设计的关键,访问容器的节点
_Tp* _M_cur;//指向缓冲区当前的元素
_Tp* _M_first;//指向缓冲区的头(起始地址)
_Tp* _M_last;//指向缓冲区的尾(结束地址)
_Map_pointer _M_node;//指向中控器的相应节点
...
};
为了实现能够随机访问,deque的中控器、缓冲区和迭代器之间的相互关系如下图所示:
以下是对迭代器源码的剖析:
//* deque是分段连续线性空间,为了能够像连续线性空间那样访问数据,
//* deque采用了一种中控方法map,map是块连续的空间,其每个元素都是一个指针,
//* 指向一块缓冲区,实质上map是T**;
//***************************************************************
// Note: this function is simply a kludge to work around several compilers'
// bugs in handling constant expressions.
//这个函数是方便不同编译器处理常量表达式的bug
inline size_t __deque_buf_size(size_t __size) {
return __size < 512 ? size_t(512 / __size) : size_t(1);
}
//deque迭代器的设计
//*deque是分段连续的线性空间,迭代器设计时必须能够进行operator++或operator--操作
//* 迭代器的功能:
//* 1、必须知道缓冲区的位置
//* 2、能够判断是否处于其所在缓冲区的边界
//* 3、能够知道其所在缓冲区当前所指位置的元素
//********************************************
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)); }
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;//指向中控器的相应节点
_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) {}
//****************************************************************************
//*********************以下是迭代器_Deque_iterator的操作**********************
//* 这些操作符的重载方便我们访问容器的内容
//* 也是让deque从接口看来是维护连续线性空间的关键
//****************************************************************************
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);//切换至下一缓冲区(节点)
_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;
}
//以下实现随机存取,迭代器可以直接跳跃n个距离
//将迭代器前移n个距离,当n负值时就为下面的operator-=操作
_Self& operator+=(difference_type __n)
{
difference_type __offset = __n + (_M_cur - _M_first);//定义一个中间变量
if (__offset >= 0 && __offset < difference_type(_S_buffer_size()))
//若前移n个距离后,目标依然在同一个缓冲区
//则直接前移n个距离
_M_cur += __n;
else {
//若前移n个距离后,目标超出该缓冲区范围
//__offset / difference_type(_S_buffer_size())计算向后移动多少个缓冲区
//-difference_type((-__offset - 1) / _S_buffer_size()) - 1计算向前移动多少个缓冲区
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;
}
//操作符+重载
//返回操作之后的副本
_Self operator+(difference_type __n) const
{
_Self __tmp = *this;
//调用operator+=操作
return __tmp += __n;
}
//利用operator+=操作实现
_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); }
//调整到正确的缓冲区
//切换到正确的元素位置
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容器具有维护
map
和迭代器的功能,deque定义的两个迭代器分别是
start
和
finish
,分别指向第一个缓冲区的第一个元素和最后一个缓冲区的最后一个元素的下一个位置;
template <class _Tp, class _Alloc>
class _Deque_base {
...
protected:
_Tp** _M_map;
size_t _M_map_size;
iterator _M_start;
iterator _M_finish;
...
};
//deque容器的定义
//配置器默认为第二级配置器
template <class _Tp, class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) >
class deque : protected _Deque_base<_Tp, _Alloc> {
// requirements:
...
public: // Iterators
typedef typename _Base::iterator iterator;
typedef typename _Base::const_iterator const_iterator;
protected:
//下面是deque的数据结构
using _Base::_M_map;//指向中控器map
using _Base::_M_map_size;//map内指针的个数
using _Base::_M_start;//指向第一个节点
using _Base::_M_finish;//指向最后一个节点
...
};
容器的结构图如下所示:
deque容器的构造函数和析构函数
了解deque容器的构造函数和析构函数,方便我们使用;其源码如下:
public:
//**********************************************************
//* 以下是构造函数和析构函数
//* 默认构造函数
//* explicit deque( const Allocator& alloc = Allocator() );
//* 容器大小为count个初始值为value的元素
//* explicit deque( size_type count,
//* const T& value = T(),
//* const Allocator& alloc = Allocator());
//* deque( size_type count,
//* const T& value,
//* const Allocator& alloc = Allocator());
//* 容器大小为count个默认值的元素
//* explicit deque( size_type count );
//* 拷贝构造函数
//* deque( const deque& other );
//* deque( const deque& other, const Allocator& alloc );
//* 初始值为[first,last)内容的容器
//* template< class InputIt >
//* deque( InputIt first, InputIt last,
//* const Allocator& alloc = Allocator() );
//* 析构函数
//* ~deque()
//*
//********************************************************
// Constructor, destructor.
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); }
explicit deque(size_type __n) : _Base(allocator_type(), __n)
{ _M_fill_initialize(value_type()); }
#ifdef __STL_MEMBER_TEMPLATES
// Check whether it's an integral type. If so, it's not an iterator.
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 _Integer>
void _M_initialize_dispatch(_Integer __n, _Integer __x, __true_type) {
_M_initialize_map(__n);
_M_fill_initialize(__x);
}
template <class _InputIter>
void _M_initialize_dispatch(_InputIter __first, _InputIter __last,
__false_type