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的结构复杂了许多,不过也不算难理解,主要是迭代器和元素的对应关系要理清。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值