Deque源码分析

deque在实现时使用一连串固定大小的数组和一个书签来实现,其结构图如下:在这里插入图片描述
假设此时我们存储的数据类型为T,那么Nodes数组中存储的类型便为T*,即T[]的位置。而T[]则是实际存储数据的内存位置。这种结构在empty()时,其Nodes下不会挂载任何数组,当我们需要插入元素时,会在Nodes的中间开始挂载而不是两端进行。这样便保证了deque可以进行两端的任意插入操作。插入操作时,会先判断当前数组是否还有剩余空间,如果当前数组还剩余空间则在数组空间内placement construct,否则我们需要申请一个新的数组空间并挂载,然后在新的数组空间进行插入操作。

Deque::iterator

Deque的迭代器中存储了节点的位置信息,利用这些位置信息,迭代器可以找到其previous和next,并且Deque的迭代器是一个随机访问迭代器,所以他还能够在O(1)的时间内找到相对于此节点的任意一个节点。
其包含如下数据。

// typedef _Tp*					    _Elt_pointer;
// typedef _Tp**					_Map_pointer;
      _Elt_pointer _M_cur;
      _Elt_pointer _M_first;
      _Elt_pointer _M_last;
      _Map_pointer _M_node;

_M_node 指向上图的某一数组在nodes中对应的元素位置,用于获取当前指向的数组的具体位置。_M_first指向数组中第一个元素,_M_last指向数组中最后一个元素,_M_cur指向当前的元素。
以iterator的移动操作为例进行分析。当需要对迭代器进行移动时,我们需要考虑两种情况。1、如果移动的距离很小,使得仅在数组内部进行移动时我们仅需要移动_M_cur。2、如果移动距离过大,那么我们还需要在数组间进行移动,这时我们需要计算目标数组的位置和新的_M_cur,并且重新设置_M_node_M_last_M_first_M_cur


_Self&
      operator+=(difference_type __n) _GLIBCXX_NOEXCEPT                 // __n可以为负值,表示向左进行偏移
      {
   
	const difference_type __offset = __n + (_M_cur - _M_first);       // 获取从_M_first 到目标位置的全部偏移量
	if (__offset >= 0 && __offset < difference_type(_S_buffer_size()))      // 目标位置位于同一个数组, _S_buffer_size()返回数组中包含的元素个数
	  _M_cur += __n;                                                        // 直接利用_M_cur进行偏移计算
	else
	  {
   
	    const difference_type __node_offset =                         // 此处用于获取nodes的偏移量,公式为   偏移量 / 数组中包含元素的个数
	      __offset > 0 ? __offset / difference_type(_S_buffer_size())  // offset为正数时使用此方式
			   : -difference_type((-__offset - 1)                 
					      / _S_buffer_size()) - 1;            // 为负数时使用此
	    _M_set_node(_M_node + __node_offset);                         // 移动_M_node,其偏移量为刚才计算的__node_offset
	    _M_cur = _M_first + (__offset - __node_offset           
				 * difference_type(_S_buffer_size()));          // 设置_M_cur, _M_first 和 _M_last 在 _M_set_node时已经被设置完成
	  }
	return *this;
      }

    void
      _M_set_node(_Map_pointer __new_node) _GLIBCXX_NOEXCEPT
      {
   
	_M_node = __new_node;
	_M_first = *__new_node;
	_M_last = _M_first + difference_type(_S_buffer_size());
      }

operator*()和operator->()证明_M_cur指向的就是实际的元素

      reference
      operator*() const _GLIBCXX_NOEXCEPT
      {
    return *_M_cur; }

      pointer
      operator->() const _GLIBCXX_NOEXCEPT
      {
    return _M_cur; }

container

Deque大量操作都依赖迭代器完成,其内部也创建了两个iterator用于指示当前已经存储元素的头和尾。内部维护的数据如下。
_M_map指向Nodes数组,也就是索引数组。_M_map_size存储索引数组的大小。_M_start存储队列的首部元素迭代器,_M_finish存储的为尾部元素迭代器。

	_Map_pointer _M_map;
	size_t _M_map_size;
	iterator _M_start;
	iterator _M_finish;
constructor

default constructor调用栈如下,在_Deque_base()构造中调用了_M_initialize_map(0),其完成了构造过程中的主要工作。

	  deque() : _Base() {
    }

      _Deque_base()
      : _M_impl()
      {
    _M_initialize_map(0); }
	 
	  _Deque_impl()
	   : _Tp_alloc_type(), _M_map(), _M_map_size(0),
	   _M_start(), _M_finish()
	   {
    }	  

_M_initialize_map(__n)函数意义为在deque中分配__n个元素所需的内存空间。构造时会先初始化_M_map所指向的内存空间,在构造时存在最小构造量8。初始化数组索引之后,再去分配所需的数组空间,其分配原则为只分配所需大小,使用lazy模式。

  template<typename _Tp, typename _Alloc>
    void
    _Deque_base<_Tp, _Alloc>::
    _M_initialize_map(size_t __num_elements)
    {
   
      const size_t __num_nodes = (__num_elements/ __deque_buf_size(sizeof(_Tp))     // __deque_buf_size()用于获取存储数据的数组大小
				  + 1);                                                     // __num_node表示共需构造多少数组才能完全存储__num_elements数据
      
      this->_M_impl._M_map_size = std::max((size_t) _S_initial_map_size,            // enum { _S_initial_map_size = 8 }; 此为常量 8
					   size_t(__num_nodes + 2));                          // _M_map数组大小至少为8,即存在最小构造量
      this->_M_impl._M_map = _M_allocate_map(this->_M_impl._M_map_size);            // 为_M_map分配存储空间

      // For "small" maps (needing less than _M_map_size nodes), allocation
      // starts in the middle elements and grows outwards.  So nstart may be
      // the beginning of _M_map, but for small maps it may be as far in as
      // _M_map+3.

      _Map_pointer __nstart = (this->_M_impl._M_map
			       + (this->_M_impl._M_map_size - __num_nodes) / 2);          // 此计算位置的结果为_M_map的中间位置 -  __num_nodes / 2
      _Map_pointer __nfinish = __nstart + __num_nodes;                              // _nfinish同理

      __try
	{
    _M_create_nodes(__nstart, __nfinish); }                                     // 创建数据存储数组
      __catch(...)
	{
   
	  _M_deallocate_map(this->_M_impl._M_map, this->_M_impl._M_map_size);         // 发生异常,回收_M_map分配的内存
	  this
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值