STL学习——Deque篇

STL学习——Deque篇

  • deque概述

    deque是一种双向开口的连续线性空间。双向开口指可以在头尾两端分别做元素的插入和删除操作。虽然vector也可以在头尾两端进行操作,但是其头部操作效率极差,无法被接受。

    deque与vector最大差异:1)deque允许于常数时间内对起头端进行元素的插入或移除操作;2)deque没有所谓的容量概念,因为它是动态地以分段连续空间组合而成,随时可以增加一段新的空间并连接起来。deque不提供所谓的空间保留功能,不存在重新配置,移动数据,释放原空间操作。

    deque提供Random Access Iterator,但它的迭代器指针不是普通指针。deque复杂度大,故对deque进行操作,为了最高效率,可将deque先完整复制到一个vector上,然后将vector排序,在复制回deque。

  • deque中控器

    deque系由一段一段的定量连续空间构成。一旦有必要在deque的前端或尾端增加新空间,便配置一段定量连续空间,串接在整个deque的头端或尾端,deque的最大任务,便是在这些分段的定量连续空间上,维护其整体连续的假象,并提供随机存取的接口。避开了“重新配置,复制,释放”的轮回,代价则是复杂额迭代器架构。

    deque实现复杂:因为分段连续线性空间,需要有中央控制,为了维持整体连续假象,数据结构的设计及迭代器前进后退等操作都非常繁琐。

    deque采用一块所谓的map作为主控。此处的map是一小块连续空间,其中每个元素(此处称为一个节点,node)都是指针,指向另一段(较大的)连续线性空间,称为缓冲区。缓冲区才是deque的存储空间主体。SGI STL中缓冲区大小可以指定,默认情况下缓冲区大小为512 bytes。

    在源码实现中,map实质是T,即指针的指针**。下面是deque的部分定义

    template <class _Tp, class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) >
    class deque : protected _Deque_base<_Tp, _Alloc> {
      __STL_CLASS_REQUIRES(_Tp, _Assignable);
    
      typedef _Deque_base<_Tp, _Alloc> _Base;
    public:                         // 基本类型
      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;
    
    #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:                      // 内部类型
      typedef pointer* _Map_pointer;// 元素的指针的指针(pointer of pointer of T)
      static size_t _S_buffer_size() { return __deque_buf_size(sizeof(_Tp)); }
    
    protected:
    #ifdef __STL_USE_NAMESPACES
      using _Base::_M_initialize_map;
      using _Base::_M_create_nodes;
      using _Base::_M_destroy_nodes;
      using _Base::_M_allocate_node;
      using _Base::_M_deallocate_node;
      using _Base::_M_allocate_map;
      using _Base::_M_deallocate_map;
    
      using _Base::_M_map;           // 指向map,map是块连续的空间,其内的每个元素都是一个指针(称为节点),指向一块缓冲区
      using _Base::_M_map_size;      // map内可容纳多少指针
      using _Base::_M_start;         // 表现第一个节点
      using _Base::_M_finish;        // 表现最后一个节点
    #endif /* __STL_USE_NAMESPACES */
    ...
    };
    
  • deque迭代器

    deque是分段连续空间。维持其“整体连续”假象的任务,落在迭代器的operator++和operator–两个运算子身上。

    deque数据结构满足条件:1)必须能够指出分段连续空间(亦即缓冲区)在哪里;2)必须判断自己是否已经处于其所在缓冲区的边缘,如果是,一旦前进或后退时,就必须跳跃至下一个或上一个缓冲区。为了能够跳跃,deque必须随时掌握管控中心(map)。

    // 其中用来决定缓冲区大小的函数buffer_size(),调用__deque_buf_size(),后者是一个全局函数,定义如下:
    // 如果n不为0,传回n,表示buffer size由用户自定义
    // 如果n为0,表示buffer size使用默认值,那么
    // 如果sz(元素大小,sizeof(value_type))小于512,传回512/sz,
    // 如果sz不小于512,传回1。
    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 {        // 未继承std::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;            // 此迭代器所指之缓冲区的现行(current)元素
      _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<>成功运作的关键。
      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个距离
      _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;
      }
    
      _Self operator+(difference_type __n) const
      {
        _Self __tmp = *this;
        return __tmp += __n;        // 调用operator+=
      }
      // 将n变为-n就可以使用operator+=()函数实现operator-=()操作
      _Self& operator-=(difference_type __n) { return *this += -__n; }
    
      _Self operator-(difference_type __n) const {
        _Self __tmp = *this;
        return __tmp -= __n;        // 调用operator-=
      }
    
      // 以下实现随机存取。迭代器可以直接跳跃n个距离
      reference operator[](difference_type __n) const { return *(*this + __n); }   // 调用operator*, operator+
    
      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); }
    
      // deque在进行加,减等操作时,遇到缓冲区边缘,需视前进或后退而定,可能需要调用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());
      }
    };
    
  • deque数据结构

    deque不仅需要维护一个map的指针,还需要维护start,finish两个迭代器,分别指向第一缓冲区的第一个元素和最后一个缓冲区的最后一个元素(的下一个位置)。此外,还需记住当前map大小,用于当map节点不足时,配置更大的一块map。

    // deque成员变量
      using _Base::_M_map;           // 指向map,map是块连续的空间,其内的每个元素都是一个指针(称为节点),指向一块缓冲区
      using _Base::_M_map_size;      // map内可容纳多少指针
      using _Base::_M_start;         // 表现第一个节点
      using _Base::_M_finish;        // 表现最后一个节点
    // 部分基本操作
      reference front() { return *_M_start; }   // 调用__deque_iterator<>::operator[]
      reference back() {
        iterator __tmp = _M_finish;
        --__tmp;
        return *__tmp;
      }
      const_reference front() const { return *_M_start; }
      const_reference back() const {
        const_iterator __tmp = _M_finish;
        --__tmp;           // 调用__deque_iterator<>::operator--
        return *__tmp;     // 调用__deque_iterator<>::operator*
        // 以上三行何不改为:return *(finish-1),因为__deque_iterator<>没有为(finish-1)定义运算子
      }
      // 当前元素拥有的元素个数,调用了迭代器重载的operator-操作
      size_type size() const { return _M_finish - _M_start; }   // 调用iterator::operator-
      size_type max_size() const { return size_type(-1); }
      // 判断deque是否为空,只有一个缓冲区
      bool empty() const { return _M_finish == _M_start; }
    
  • deque构造与内存管理

    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() {
        _M_initialize_map(__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;          // 双指针,用于控制中央控制器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结构
    template <class _Tp, class _Alloc>
    void
    _Deque_base<_Tp,_Alloc>::_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);
      _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内的每个现用节点配置缓冲区。所有缓冲区加起来就是deque的可用空间(最后一个缓冲区可能留有一些富裕)
        _M_create_nodes(__nstart, __nfinish);
      }
      __STL_UNWIND((_M_deallocate_map(_M_map, _M_map_size),
                    _M_map = 0, _M_map_size = 0));
      // 为deque内的两个迭代器start和end设定正确内容
      _M_start._M_set_node(__nstart);
      _M_finish._M_set_node(__nfinish - 1);
      _M_start._M_cur = _M_start._M_first;          // first,cur都是public
      _M_finish._M_cur = _M_finish._M_first +
                   __num_elements % __deque_buf_size(sizeof(_Tp));
      // 前面说过,如果刚好整除,会多分配一个节点
      // 此时即令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);
      }
    
    // 先配置一整块新的缓冲区,再设妥新元素内容,然后更改迭代器finish的状态
    // 只有当finish.cur == finish.last - 1时才会被调用
    // 即,只有当最后一个缓冲区只剩下一个备用元素空间时才会被调用
    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();                       // 若符合某种条件则必须重换一个map
      *(_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); // 改变finish,令其指向新节点
        _M_finish._M_cur = _M_finish._M_first;        // 设定finish的状态
      }
      __STL_UNWIND(_M_deallocate_node(*(_M_finish._M_node + 1)));
    }
    
    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);
      }
    
    // 当前状态下,第一缓冲区无备用空间。
    // 只有当start.cur == start.first时才会被调用
    // 也就是说,只有当第一个缓冲区没有任何备用元素时才会被调用
    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;      // 设定start的状态
        construct(_M_start._M_cur, __t_copy);        // 针对标的元素设值
      }
      __STL_UNWIND((++_M_start, _M_deallocate_node(*(_M_start._M_node - 1))));
    } 
    
    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尾端的节点备用空间不足
          // 符合以上条件则必须重换一个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))
          // 如果map前端的节点备用空间不足
          // 符合以上条件则必须重换一个map(配置更大的,拷贝原来的,释放原来的)
          _M_reallocate_map(__nodes_to_add, true);
      }
    
    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) {
        __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 {
        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);         // 配置一块空间,准备给新map使用
        __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);      // 把原map内容拷贝过来
        _M_deallocate_map(_M_map, _M_map_size);                           // 释放原map
    
        _M_map = __new_map;                                               // 设置新map的起始地址和大小
        _M_map_size = __new_map_size;
      }
    
      _M_start._M_set_node(__new_nstart);                                 // 重新设定迭代器start和finish
      _M_finish._M_set_node(__new_nstart + __old_num_nodes - 1);
    }
    
  • deque基本操作

    deque的基本操作主要有pop_back,pop_front,clear,erase,insert等。

    // pop为将元素拿掉,包括从最前端和最尾端取元素。注意某些条件下,需要释放缓冲区。
     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();                            // 这里将进行缓冲区的释放工作
      }
    // 只有当finish.cur == finish.first时才会被调用
    template <class _Tp, class _Alloc>
    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();                           // 这里讲进行缓冲区的释放工作
      }
    // 只有当start.cur == start.las - 1时才会被调用
    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);     // 调整start的状态,使指向下一个缓冲区的第一个元素
      _M_start._M_cur = _M_start._M_first;
    } 
    
    // 清除整个deque。注意:deque的最初状态(无任何元素时)保有一个缓冲区,因此,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());    // 将缓冲区内的所有元素析构
        _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;                              // 调整状态
    } 
    
    // 清除pos所指的元素。pos为清除点
      iterator erase(iterator __pos) {
        iterator __next = __pos;
        ++__next;
        difference_type __index = __pos - _M_start;       // 清除点之前的元素个数
        if (size_type(__index) < (this->size() >> 1)) {   // 如果清除点之前的元素比较少
          copy_backward(_M_start, __pos, __next);         // 就移动清除点之前的元素
          pop_front();                                    // 移动完毕,最后一个元素冗余,去除之
        }
        else {                                            // 清除点之后的元素比较少
          copy(__next, _M_finish, __pos);                 // 就移动清除点之后的元素
          pop_back();                                     // 移动完毕,最后一个元素冗余,去除之
        }
        return _M_start + __index;
      }
    
    // 清除[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) {   // 如果清除区间就是整个deque
        clear();                                          // 直接调用clear()即可
        return _M_finish;
      }
      else {
        difference_type __n = __last - __first;                            // 清除区间的长度
        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;                           // 标记deque的新起点
          destroy(_M_start, __new_start);                                  // 移动完毕,将冗余的元素析构
          _M_destroy_nodes(__new_start._M_node, _M_start._M_node);         // 将冗余的缓冲区释放
          _M_start = __new_start;                                          // 设定deque的新起点
        }
        else {                                                             // 如果清除区间后的元素比较少
          copy(__last, _M_finish, __first);                                // 向前移动前方元素(覆盖清除区间)
          iterator __new_finish = _M_finish - __n;                         // 标记deque的新起点
          destroy(__new_finish, _M_finish);                                // 移动完毕,将冗余的元素析构
          _M_destroy_nodes(__new_finish._M_node + 1, _M_finish._M_node + 1); // 释放冗余的缓冲区
          _M_finish = __new_finish;                                        // 设定deque的新尾点
        }
        return _M_start + __elems_before;
      }
    }
    
     // 在position处插入一个元素,其值为x
      iterator insert(iterator position, const value_type& __x) {
        if (position._M_cur == _M_start._M_cur) {         // 如果插入点是deque最前端
          push_front(__x);                                // 交给push_front去做
          return _M_start;
        }
        else if (position._M_cur == _M_finish._M_cur) {   // 如果插入点是deque最尾端
          push_back(__x);                                 // 交给push_back去做
          iterator __tmp = _M_finish;
          --__tmp;
          return __tmp;
        }
        else {
          return _M_insert_aux(position, __x);            // 交给insert_aux去做
        }
      }
    // 插入辅助函数
    template <class _Tp, class _Alloc>
    typename deque<_Tp, _Alloc>::iterator
    deque<_Tp,_Alloc>::_M_insert_aux(iterator __pos, const value_type& __x)
    {
      difference_type __index = __pos - _M_start;    // 插入点之前的元素个数
      value_type __x_copy = __x;
      if (size_type(__index) < this->size() / 2) {   // 如果插入点之前的元素个数比较少
        push_front(front());                         // 在最前端加入与第一元素同值的元素
        iterator __front1 = _M_start;                // 以下标示标记,然后进行元素移动
        ++__front1;
        iterator __front2 = __front1;
        ++__front2;
        __pos = _M_start + __index;
        iterator __pos1 = __pos;
        ++__pos1;
        copy(__front2, __pos1, __front1);            // 元素移动
      }
      else {                                         // 插入点之后的元素个数比较少
        push_back(back());                           // 在最尾端加入与最后元素同值的元素
        iterator __back1 = _M_finish;                // 以下标示记号,然后进行元素移动
        --__back1;
        iterator __back2 = __back1;
        --__back2;
        __pos = _M_start + __index;
        copy_backward(__pos, __back2, __back1);      // 元素移动
      }
      *__pos = __x_copy;                             // 再插入点上设定新值
      return __pos;
    }
    
  • 参考文献

    STL源码剖析——侯捷

    STL源码

时间: 2024-11-10 00:33:52

STL学习——Deque篇的相关文章

STL学习——Priority_queue篇

STL学习--Priority_queue篇 概述 priority_queue是一个拥有权值观念的queue,它允许加入新元素,移除旧元素,审视元素值等功能.因为它是queue,故只允许底端加入元素,顶端取出元素.priorit_queue内元素并非依照被推入的次序排列,而是依照元素权值排列.权值最高者,排在最前面. 实现 priority_queue利用max_heap和vector表现的完全二叉树实现.它的实现完全以底部容器为根据,并使用heap处理规则,故实现简单.默认情况,使用vect

STL学习——Vector篇

STL学习--Vector篇 vector简介 vector的数据安排及操作方式与array非常相似,两者的区别在于空间运用的灵活性.array是静态空间,一旦配置了,就不能改变:要换个大(或小)一点的可以,但琐碎的事由客户端完成:首先配置一块新空间,然后将元素从旧址一一搬往新址,再把原来的空间释还给系统.而vector是动态空间,随着元素的加入,它的内部机制会自动扩充空间以容纳新元素.它对内存的合理利用和灵活运用有很大的帮助. vector实现关键技术:对大小的控制以及重新配置时的数据移动效率

STL学习_List篇

STL学习--List篇 简介 List与Vector不同,它不使用连续空间,而是每次插入或删除一个元素,就配置或释放一个元素空间.故list对空间的使用精准,不浪费任何空间.list对任何位置的元素插入或删除,常数时间完成. List与Vector的使用,视元素的多少,元素的构造复杂度,元素存取行为的特性而定. List节点 List本身与List节点不同,List是一个双向的链表.其节点信息如下: struct _List_node_base { _List_node_base* _M_ne

STL学习_配接器篇

STL学习_配接器篇 定义 配接器(Adapter)在STL组件的灵活组合运用功能上,扮演着轴承.转换器的角色.它事实上是一种设计模式.即将一个class的接口转换为另一个class的接口,使原本因接口不兼容而不能合作的classes,可以一起运作. 分类 STL所提供的各种适配器中,改变仿函数(functors)接口者,称为function adapter:改变容器(containers)接口者,称为container adapter:改变迭代器(iterators)接口者,称为iterato

STL学习——Stack/Queue篇

STL学习--Stack/Queue篇 Stack 概述 stack是一种先进先出的数据结构,只有一个出口,stack允许新增元素,移除元素,取得最顶端元素.但除了最顶端外,没有任何办法可以存取stack其他元素.即不允许遍历行为. 实现 stack实现是以容器为底部结构的,将容器的接口改变,使其符合"先进先出"特性,便形成了一个栈.具体实现可以将底部deque的头端开口封闭,便实现了stack.因为stack具有"修改某物接口,形成另一种风貌"性质,故称为&quo

C++ STL学习——queue

我们在上一篇博客中<C++ STL学习--stack>简单介绍了STL 中stack这种数据结构的使用,这篇博客主要来讲一下queue队列的使用.其实queue的使用和stack一样简单.示例代码上传至 https://github.com/chenyufeng1991/STL_queue . (1)首先要引入头文件  #include <queue> . 并使用命名空间  using namespace std; (2)同stack一样,queue也不能使用迭代器.因为queue

STL之deque容器的实现框架

说明:本文仅供学习交流,转载请标明出处,欢迎转载! vector底层采用的是一个数组来实现,list底层采用的是一个环形的双向链表实现,而deque则采用的是两者相结合,所谓结合,并不是两种数据结构的结合,而是某些性能上的结合.我们知道,vector支持随机访问,而list支持常量时间的删除,deque支持的是随机访问以及首尾元素的删除. deque是double ended queue的缩写,读作deck.首先我们用一个图来说明deque底层所采用的数据结构. 这个数据结构有一种似曾相识的感觉

stl学习记录

Effective STL 中文版学习记录 条款4 判断容器是否为空 使用empty而不是size().size()操作在实现上不是一个时间常数操作条款5 尽量使用区间成员函数代替它们的单元素兄弟.STL实现中,区间范围显示比单个循环操作更优化 条款7:当使用new得指针的容器时,记得在销毁容器前delete那些指针vc2008下 运行代码 可以看到 该程序内存不断增加// 1111111.cpp : 定义控制台应用程序的入口点.// #include "stdafx.h" #incl

UI学习第二篇 (控件)

UIbutton 也是一个控件,它属于UIControl 用的最多的就是事件响应 1. //创建按钮对象 UIButton * _botton = [UIButton buttonWithType:UIButtonTypeCustom]; //设置标题 [_botton setTitle:@"按住说话" forstate:UIControlStateNormal]; [_botton setTitle:@"松开说话" forstate:UIControlStateH