STL源码阅读-deque

本文详细解读deque数据结构的实现原理,包括其内存组织、迭代器操作、插入、删除、清空等核心功能的实现细节。deque作为提供双向开口连续线性空间的数据结构,支持高效地进行元素的插入和移除操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">vector提供单向开口的线性空间,而deque提供的则是双向开口的连续线性空间,当然这种线性空间并不是真正连续的。 deque允许于常数时间内对起痘团进行元素的插入或者移除操作,并且没有所谓的容量(capacity)的概念,以动态的分段连续内存空间组合而成,随时可以增加一段新的空间并连接起来。</span>

deque实现这种连续空间的组合,关键在于有一个中央控制器,从而对外呈现一个完整的整体。

去掉比较繁琐的typedef以及其他比较关键的函数的deque的定义如下,:

template <class _Tp, class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) >
class deque : protected _Deque_base<_Tp, _Alloc> {

  // requirements:

  __STL_CLASS_REQUIRES(_Tp, _Assignable);

  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;

#ifdef __STL_CLASS_PARTIAL_SPECIALIZATION
  typedef reverse_iterator<const_iterator> const_reverse_iterator;
  typedef reverse_iterator<iterator> reverse_iterator;
#else /* __STL_CLASS_PARTIAL_SPECIALIZATION */
  typedef reverse_iterator<const_iterator, value_type, const_reference, 
                           difference_type>  
          const_reverse_iterator;
  typedef reverse_iterator<iterator, value_type, reference, difference_type>
          reverse_iterator; 
#endif /* __STL_CLASS_PARTIAL_SPECIALIZATION */

protected:                      // Internal typedefs
  typedef pointer* _Map_pointer;
  static size_t _S_buffer_size() { return __deque_buf_size(sizeof(_Tp)); }

protected:


public:                         // Basic accessors
  iterator begin() { return _M_start; }
  iterator end() { return _M_finish; }
  const_iterator begin() const { return _M_start; }
  const_iterator end() const { return _M_finish; }

  reverse_iterator rbegin() { return reverse_iterator(_M_finish); }
  reverse_iterator rend() { return reverse_iterator(_M_start); }
  const_reverse_iterator rbegin() const 
    { return const_reverse_iterator(_M_finish); }
  const_reverse_iterator rend() const 
    { return const_reverse_iterator(_M_start); }

  reference operator[](size_type __n)
    { return _M_start[difference_type(__n)]; }
  const_reference operator[](size_type __n) const 
    { return _M_start[difference_type(__n)]; }
首先关注的第一个函数是_S_buffer_size(),它是对函数__deque_buf_size的简单封装。

static size_t _S_buffer_size() { return __deque_buf_size(sizeof(_Tp)); }

其中__deque_buf_size的定义如下:

inline size_t __deque_buf_size(size_t __size) {
  return __size < 512 ? size_t(512 / __size) : size_t(1);
}
从上面可以看到,deque定义的缓冲区大小是512bytes,如果对象tp的大小大于512bytes,那么每个缓冲区的容量就只有一个tp,否则就是(512/sizeof(tp))个对象了。


deque的结构比较复杂,要理解deque设计细节,首先需要理解deque在内存组织上的特点,下面是deque组织结构示意图。


map是一个连续的内存空间,用于存储缓存空间的首地址,map的地址不够用的时候,就会重新申请map的空间,并且将原来存储的地址更新到新的位置。

在讨论deque实现细节之前,先从宏观上看一下如果希望实现上述的组织结构图,那么应该怎么做呢:

1.在尾部增加一个数据,如果数据空间够用,那么直接增加,finish->cur++就可以了;如果存储空间不够,需要申请新的内存,并且需要在map中注册,同时更新finish,指向新的缓存空间;如果此时map的存储空间不够用了,就应当申请新的map空间,将map的地址扩展过去,并且更新start和finish的node节点指针的值,按照这种思路,如果插入数据之后,那么原来的iterator会失效,因为iterator的node节点的值有可能会被修改,这样就没有办法跳转到下一个节点了。。。。好悲惨的说

2.如果在首部增加一个数据,如果存储空间够用,那么直接增加,start->cur--就可以了,如果存储空间不够用,就需要申请新的存储空间了,这个过程与在尾部增加一个数据差不多。

分析deque的内部实现细节,从deque的构造函数开始,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); }
  explicit deque(size_type __n) : _Base(allocator_type(), __n)
    { _M_fill_initialize(value_type()); }
可以看到,这个初始化里面都调用了函数_Base进行自开始的空间初始化。_Base的定义如下:

typedef _Deque_base<_Tp, _Alloc> _Base;
然后,看一下_Deque_base的定义,
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() {
    _M_initialize_map(__num_elements);//根据__num_elements进行初始化存储空间
  }
  _Deque_base(const allocator_type&)
    : _M_map(0), _M_map_size(0),  _M_start(), _M_finish() {}
  ~_Deque_base();    

protected:
  void _M_initialize_map(size_t);
  void _M_create_nodes(_Tp** __nstart, _Tp** __nfinish);
  void _M_destroy_nodes(_Tp** __nstart, _Tp** __nfinish);
  enum { _S_initial_map_size = 8 };

protected:
  _Tp** _M_map;
  size_t _M_map_size;  
  iterator _M_start;
  iterator _M_finish;

  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))); }
  _Tp** _M_allocate_map(size_t __n) 
    { return _Map_alloc_type::allocate(__n); }
  void _M_deallocate_map(_Tp** __p, size_t __n) 
    { _Map_alloc_type::deallocate(__p, __n); }
};

从上面可以看到,deque在初始化的时候,会调用父类_Deque_base里面的构造函数_Deque_base(const allocator_type&, size_t __num_elements),这个函数最终调用的是 _M_initialize_map(__num_elements);。为了表述方便,M空间为存储map索引的空间,D空间为存储实际数据的缓存空间。该函数的原形如下:

template <class _Tp, class _Alloc>
void
_Deque_base<_Tp,_Alloc>::_M_initialize_map(size_t __num_elements)
{
  size_t __num_nodes = 
    __num_elements / __deque_buf_size(sizeof(_Tp)) + 1;//分配缓存空间,至少有一个单位的缓存空间

  _M_map_size = max((size_t) _S_initial_map_size, __num_nodes + 2);//enum { _S_initial_map_size = 8 };,最小的map M空间是8个。
  _M_map = _M_allocate_map(_M_map_size);//申请存放map的M空间

  _Tp** __nstart = _M_map + (_M_map_size - __num_nodes) / 2;
  _Tp** __nfinish = __nstart + __num_nodes;
    
  __STL_TRY {
    _M_create_nodes(__nstart, __nfinish);//为start与finish之间的map分配对应的D空间。
  }
  __STL_UNWIND((_M_deallocate_map(_M_map, _M_map_size), 
                _M_map = 0, _M_map_size = 0));
  _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));//最后一个节点中的cur指向的空间位置,应当是去掉整数deque_buf_size剩余的。
}
可以看出来,deque在初始化的时候,D空间会比需求的多一个,至少会分配一个缓存D空间,以及至少8个存储map索引的M空间,如果需要的map节点大于8个,那么会分配的存储空间S+2个空间。上面的函数里面,调用函数 _M_allocate_map用于分配map索引的存储空间,该函数原型如下,直接调用alloc用来分配空间的。

typedef simple_alloc<_Tp*, _Alloc> _Map_alloc_type;
_Tp** _M_allocate_map(size_t __n) 
    { return _Map_alloc_type::allocate(__n); }
此外,上面的函数里面,还调用了函数_M_create_nodes用于分配缓存D空间,该函数的原形如下:

template <class _Tp, class _Alloc>
void _Deque_base<_Tp,_Alloc>::_M_create_nodes(_Tp** __nstart, _Tp** __nfinish)//传入的参数为[nstart,nfinish)
{
  _Tp** __cur;
  __STL_TRY {
    for (__cur = __nstart; __cur < __nfinish; ++__cur)
      *__cur = _M_allocate_node();//直接调用createnode函数//分配用于缓存的D空间
  }
  __STL_UNWIND(_M_destroy_nodes(__nstart, __cur));
}

//调用alloc来分配存储空间
typedef simple_alloc<_Tp, _Alloc>  _Node_alloc_type;

_Tp* _M_allocate_node()
    { return _Node_alloc_type::allocate(__deque_buf_size(sizeof(_Tp))); }
到这里,基本上deque的空间分配算是完结了,如果需要插入新的数据,当空间不足时,其实还是一样的会进行类似的数据分配。只不过需要修改map索引以及更新一些其他的标签。
要实现对于deque的访问,最离不开的是iterator,它是保障deque对外呈现单一连续空间的工程,iterator的部分定义如下:

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;//当前D空间中使用的地址
  _Tp* _M_first;//标记当前D空间的起始地址
  _Tp* _M_last;//标记当前D空间的完结地址
  _Map_pointer _M_node;//标记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 */

从最上面的deque的结构组织图实现可以看出,iterator在实现的时候,利用cur、first、last以及node来实现map、以及缓存D空间之间的胶合的。

iterator最重要的就是++、--等操作了。逐一看一下这些函数的实现。

  reference operator*() const { return *_M_cur; }//返回iterator指向的缓存D空间的值
#ifndef __SGI_STL_NO_ARROW_OPERATOR
  pointer operator->() const { return _M_cur; }//返回iterator指向的缓存D空间的指针
#endif /* __SGI_STL_NO_ARROW_OPERATOR */


判断两个iterator之间的距离:

difference_type operator-(const _Self& __x) const {
    return difference_type(_S_buffer_size()) * (_M_node - __x._M_node - 1) +//-1是因为后面的计算多了1个buffer的距离
      (_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;
  }
_M_set_node函数定义如下;

  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());
  }
++操作如果到达了结尾,那么直接调到下一个节点就可以了,然后重新设置一下各个标签。

--操作 的函数原型如下:

  _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;
  }
由于first和last标记的空间是[frist,last),所以++和--的操作顺序是有所不同的。

+=操作使得iterator拥有了randomaccess的功能,其原型如下:

_Self& operator+=(difference_type __n)
  {
    difference_type __offset = __n + (_M_cur - _M_first);
    if (__offset >= 0 && __offset < difference_type(_S_buffer_size()))//如果offset还在同一个节点范围内,直接加上就可以了
      _M_cur += __n;
    else {
      difference_type __node_offset =
        __offset > 0 ? __offset / difference_type(_S_buffer_size())//如果cur>0,那么需要进行偏移
                   : -difference_type((-__offset - 1) / _S_buffer_size()) - 1;//如果<0 ,<span style="font-family: Arial, Helvetica, sans-serif;">从尾部开始数的话,</span><span style="font-family: Arial, Helvetica, sans-serif;">需要-1,</span>
      _M_set_node(_M_node + __node_offset);
      _M_cur = _M_first + 
        (__offset - __node_offset * difference_type(_S_buffer_size()));//计算出相应的node值
    }
    return *this;
  }

剩下的iterator的操作就比较容易理解了,基本上都是对上面几个功能的略加封装。

_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;
  }

  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());
  }
至此,iterator的所有操作就结束了,看一下deque进行push_back、pop_back以及push_front和pop_front的操作是如何实现的。

void push_back(const value_type& __t) {
    if (_M_finish._M_cur != _M_finish._M_last - 1) {//如果cur还没有到最后一个可用的空间,可以直接使用
      construct(_M_finish._M_cur, __t);
      ++_M_finish._M_cur;
    }
    else//已经到了最后一个可用空间,那么需要重新分配下一个空间了,因为finish.cur指向的是最后一个可用空间的下一个。
      _M_push_back_aux(__t);
  }
push_back如果空间不足的话,会调用_M_push_back_aux,其原型如下:

template <class _Tp, class _Alloc>
void deque<_Tp,_Alloc>::_M_push_back_aux(const value_type& __t)
{
  value_type __t_copy = __t;
  _M_reserve_map_at_back();//调整指针的位置
  *(_M_finish._M_node + 1) = _M_allocate_node();//申请新的空间
  __STL_TRY {
    construct(_M_finish._M_cur, __t_copy);//利用完最后一个空间
    _M_finish._M_set_node(_M_finish._M_node + 1);//更新的参数
    _M_finish._M_cur = _M_finish._M_first;//cur指向下一个可用的空间
  }
  __STL_UNWIND(_M_deallocate_node(*(_M_finish._M_node + 1)));
}
_M_reserve_map_at_back()用于调整map的地址,原型如下:

 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))//如果已经超出了当前的map可用空间了,需要重新分配
      _M_reallocate_map(__nodes_to_add, false);
  }
_M_reallocate_map函数主要是用来调整空间大小,首先需要申请一段空间,然后将原来的指针什么的都调整过去,再进行销毁,在这个过程中,应该只有finish和start的指针参数继续保持有效。

template <class _Tp, class _Alloc>
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) {//如果剩余空间够用,就直接调整一下start和finish区间的位置
    __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 {//申请新的存储空间,然后将数据拷贝过去,并更新start和finish的值。
    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);
    copy(_M_start._M_node, _M_finish._M_node + 1, __new_nstart);
    _M_deallocate_map(_M_map, _M_map_size);

    _M_map = __new_map;
    _M_map_size = __new_map_size;
  }

  _M_start._M_set_node(__new_nstart);
  _M_finish._M_set_node(__new_nstart + __old_num_nodes - 1);
}

至此,push_back的所有操作基本都已经实现了。

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 <class _Tp, class _Alloc>
void  deque<_Tp,_Alloc>::_M_push_front_aux(const value_type& __t)
{
  value_type __t_copy = __t;
  _M_reserve_map_at_front();//调整map的头部
  *(_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;
    construct(_M_start._M_cur, __t_copy);
  }
  __STL_UNWIND((++_M_start, _M_deallocate_node(*(_M_start._M_node - 1))));
} 
_M_reserve_map_at_front函数原型如下:

 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);//增加map节点
  }
这部分的操作和上面push_back的操作还是基本类似的。
push_back和push_front的操作到这里基本就完成了,这一对操作基本类似,在增加数据之前,需要判断是否有超过左右边界,[start,finish),以及[fist,last)的开闭需要比较注意。所以在具体的实现的时候,略有不同。

与push_back和push_front相对的是pop_back和pop_front,这两个函数如下,可以对比来看:

pop_back()

 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();
  }

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();
  }

这两个函数分别调用了_M_pop_back_aux和_M_pop_front_aux函数,其原型如下:

_M_pop_back_aux函数

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);
  _M_finish._M_cur = _M_finish._M_last - 1;
  destroy(_M_finish._M_cur);
}
_M_pop_front_aux函数
template <class _Tp, class _Alloc>
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);
  _M_start._M_cur = _M_start._M_first;
}      
这两个操作基本差不多,都是直接删除申请的存储空间,然后移动指针。


下面这个是clear()函数,用于清除整个deque。deque最初的状态是保留一个缓冲区,因此,clear之后恢复最初的状态,也是需要保留一个缓冲区的。 这里有一个疑问啊,如果一直使用pop_back或者pop_front是不是会导致空间全没有了,经过测试貌似还真是没有了额。。。这个待确认。。。

clear的函数原型如下:

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());//调用的destroy函数进行析构
    _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;//调整状态
}

erase操作,清除[first,last)区间内的所有元素。

template <class _Tp, class _Alloc>
typename deque<_Tp,_Alloc>::iterator 
deque<_Tp,_Alloc>::erase(iterator __first, iterator __last)
{
  if (__first == _M_start && __last == _M_finish) {//如果需要清除的是整个区间,那么就直接调用clear()
    clear();
    return _M_finish;
  }
  else {
    difference_type __n = __last - __first;//清除区间的长度,由于有iterator,可以很方便的就知道删除那些空间了。。
    difference_type __elems_before = __first - _M_start;
    if (__elems_before < difference_type((this->size() - __n) / 2)) {//清除数据的前方个数比较少
      copy_backward(_M_start, __first, __last);
      iterator __new_start = _M_start + __n;
      destroy(_M_start, __new_start);
      _M_destroy_nodes(__new_start._M_node, _M_start._M_node);
      _M_start = __new_start;
    }
    else {
      copy(__last, _M_finish, __first);
      iterator __new_finish = _M_finish - __n;
      destroy(__new_finish, _M_finish);
      _M_destroy_nodes(__new_finish._M_node + 1, _M_finish._M_node + 1);
      _M_finish = __new_finish;
    }
    return _M_start + __elems_before;
  }
}
最后一个函数insert,很不容易啊。。。 直接看最简单的版本的insert,其实都差不多的,为了适应不同的输入参数。

template <class _Tp, class _Alloc>
void deque<_Tp, _Alloc>::insert(iterator __pos,
                                const value_type* __first,
                                const value_type* __last) {
  size_type __n = __last - __first;//插入数据的个数
  if (__pos._M_cur == _M_start._M_cur) {//在开头插入数据
    iterator __new_start = _M_reserve_elements_at_front(__n);//调整之前数据空间
    __STL_TRY {
      uninitialized_copy(__first, __last, __new_start);//将数据拷贝过去,因为有iterator,所以拷贝的时候可以当做是连续存储空间
      _M_start = __new_start;
    }
    __STL_UNWIND(_M_destroy_nodes(__new_start._M_node, _M_start._M_node));
  }
  else if (__pos._M_cur == _M_finish._M_cur) {//在末尾插入数据
    iterator __new_finish = _M_reserve_elements_at_back(__n);//在增加数据空间
    __STL_TRY {
      uninitialized_copy(__first, __last, _M_finish);//拷贝过去
      _M_finish = __new_finish;
    }
    __STL_UNWIND(_M_destroy_nodes(_M_finish._M_node + 1, 
                                  __new_finish._M_node + 1));
  }
  else
    _M_insert_aux(__pos, __first, __last, __n);//调用标准函数
}

_M_insert_aux函数原型如下:

template <class _Tp, class _Alloc>
void deque<_Tp,_Alloc>::_M_insert_aux(iterator __pos,
                                      const value_type* __first,
                                      const value_type* __last,
                                      size_type __n)
{
  const difference_type __elemsbefore = __pos - _M_start;//在pos之前的数据的个数
  size_type __length = size();
  if (__elemsbefore < __length / 2) {//如果前面的数据小于总的数据的一半,那么应该移动前面的数据
    iterator __new_start = _M_reserve_elements_at_front(__n);//在前面增加n个缓存空间
    iterator __old_start = _M_start;
    __pos = _M_start + __elemsbefore;
    __STL_TRY {
      if (__elemsbefore >= difference_type(__n)) {
        iterator __start_n = _M_start + difference_type(__n);
        uninitialized_copy(_M_start, __start_n, __new_start);// -----|----A---|-----B---|------C----|三段数据,先将B移到A,然后将C移到B起点的位置,最后将
        _M_start = __new_start;//新的数据移动到剩下的位置。
        copy(__start_n, __pos, __old_start);
        copy(__first, __last, __pos - difference_type(__n));
      }
      else {
        const value_type* __mid = 
          __first + (difference_type(__n) - __elemsbefore);
        __uninitialized_copy_copy(_M_start, __pos, __first, __mid,
                                  __new_start);
        _M_start = __new_start;
        copy(__mid, __last, __old_start);
      }
    }
    __STL_UNWIND(_M_destroy_nodes(__new_start._M_node, _M_start._M_node));
  }
  else {
    iterator __new_finish = _M_reserve_elements_at_back(__n);
    iterator __old_finish = _M_finish;
    const difference_type __elemsafter = 
      difference_type(__length) - __elemsbefore;
    __pos = _M_finish - __elemsafter;
    __STL_TRY {
      if (__elemsafter > difference_type(__n)) {
        iterator __finish_n = _M_finish - difference_type(__n);
        uninitialized_copy(__finish_n, _M_finish, _M_finish);
        _M_finish = __new_finish;
        copy_backward(__pos, __finish_n, __old_finish);
        copy(__first, __last, __pos);
      }
      else {
        const value_type* __mid = __first + __elemsafter;
        __uninitialized_copy_copy(__mid, __last, __pos, _M_finish, _M_finish);
        _M_finish = __new_finish;
        copy(__first, __mid, __pos);
      }
    }
    __STL_UNWIND(_M_destroy_nodes(_M_finish._M_node + 1, 
                                  __new_finish._M_node + 1));
  }
}

到此位置,deque的基本原理差不多就完成了,有很多函数可能存在很多不同的参数方案,略有不同,但是大的思路是一致的,一切都是为了优化性能而实现的。deque算是比较复杂的数据组织方式,后面的stack、queue默认的底层容器就是deque,所以后面的stack、queue就直接跳过了,至于堆排序什么的,后面复习排序算法的时候,再一起进行算法总结吧。



















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值