STL deque

一.deque的数据结构

1.1结构图

       deque中主要包括一个中控器_M_map,首尾迭代器_M_start,_M_finish,两种型别的内存配置器  typedef simple_alloc<_Tp, _Alloc> _Node_alloc_type;  typedef simple_alloc<_Tp*, _Alloc> _Map_alloc_type。其结构图如下

1.2 代码设计

         接下来结合代码和上面的结构图来具体说明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;

  _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);     // 根据元素个数初始化中控器,在后面会具体介绍
  }
  ...
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;  //用于为中控器分配内存的内存配置器
  ...
};

emplate <class _Tp, class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) >
class deque : protected _Deque_base<_Tp, _Alloc> { // 继承了_Deque_base 
public:                         // Basic accessors
    deque(const value_type* __first, const value_type* __last, const allocator_type& __a = allocator_type()) 
    : _Base(__a, __last - __first)
    { uninitialized_copy(__first, __last, _M_start); }

  iterator begin() { return _M_start; }
  iterator end() { return _M_finish; }
  const_iterator begin() const { return _M_start; }
  const_iterator end() const { return _M_finish; }
  //还实现了插入删除等操作,会在后面说明

private:
};

二.deque的内存管理

2.1 内存的初始化

        我们先从deque的构造函数说起吧,如1.2中的源码所示,deque继承了_Deque_base,因此先调用了_Deque_base的构造函数,在其中调用了_M_initialized_map(),在该函数中首先分配了中控器_M_map的内存区,接着根据要放入的元素数量__num_elements,选择中控器中间的__num_elements / 缓冲区大小(以sizeof(_Tp)为单位) + 1个节点分配缓冲区内存,这个分配是调用_Node_alloc_type配置器实现的。其源码如下:

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_finish会指向多配置的这个节点,其中的_M_cur指向该缓冲区的首部
    _M_map_size = max((size_t) _S_initial_map_size, __num_nodes + 2);// 中控器最小节点数为8
    _M_map = _M_allocate_map(_M_map_size); // 调用内存配置器分配中控器map的内存空间

    // 保证初始化时使用的中控节点位于map的最中间,这可使两端的可阔空间一样大
    _Tp** __nstart = _M_map + (_M_map_size - __num_nodes) / 2; // 指向被使用的起始中控节点
    _Tp** __nfinish = __nstart + __num_nodes;                  // 指向被使用的中控节点尾部

    _M_create_nodes(__nstart, __nfinish);  // 为中控节点[__nstart, __nfinish)创建缓冲区

    _M_start._M_set_node(__nstart);        // 首部迭代器回指_M_map中指向首部缓冲区的节点
    _M_finish._M_set_node(__nfinish - 1);  // 回指_M_map中的节点
    _M_start._M_cur = _M_start._M_first;   // 首部迭代器的_M_cur指向第一个缓冲区的首字节
    _M_finish._M_cur = _M_finish._M_first + __num_elements % __deque_buf_size(sizeof(_Tp));
                                           // 尾部迭代器的_M_cur最后一个元素的后一个位置
}

template <class _Tp, class _Alloc>
void _Deque_base<_Tp,_Alloc>::_M_create_nodes(_Tp** __nstart, _Tp** __nfinish)
{
  _Tp** __cur;
  try{
    for (__cur = __nstart; __cur < __nfinish; ++__cur) //为中控节点[__nstart, __nfinish)创建缓冲区
      *__cur = _M_allocate_node();
  }
  cache(...)(_M_destroy_nodes(__nstart, __cur));  // 若非配置失败则收回以分配的缓冲区,保证了分配的原子性
}

       当在_Deque_base构造完成后,内存区的分配工作也已完毕,最后在deque的构造函数中,在分配的缓冲区内存上调用uninitialize_copy或uninitialized_fill来构造对象。至此,deque的构造过程结束,构造完成后的结构图如1.1节所示。

2.2 添加与删除

      1. 尾部或首部添加元素

if( 尾部或首部还有两个及以上的剩余空间 ) {
    调用构造函数在首部或尾部空间构造对象;
}
else {// 空间不足两个
    if(_M_map中尾部的剩余空间不足两个及以上) {
        // 调整或扩充_M_map
        if(若_M_map的当前大小大于添加元素后使用的节点数) {
           则不对_M_map进行扩充,而是将使用节点调整至_M_map的中间区域
        }
        else{
            调用内存配置器为_M_map开辟一个更大的内存;
            将_M_map中使用的节点拷贝到新的新的内存区;
        }
    }
    在缓冲区的下一个(前一个)可用节点上调用构造函数
}

      2. 尾部或首部删除元素

if(尾部迭代器所指缓冲区中所剩元素大于等于1) { // 通过迭代器中的_M_cur != _M_first判断
    析构掉最后一个元素,并移动_M_finish->_M_cur;
}
else{  // 该缓冲区已无元素
    销毁掉该缓冲区;
    将_M_finish移动到前一个缓冲区,并析构掉最后一个元素
}

      3.在中间删除元素

if(删除点之前的元素较少) {
    调用copy_backward将元素后移;
    再调用pop_front将最前面的一个元素移出;
}
else { // 删除点之后的元素较少
    调用copy将删除点之后的元素前移;
    调用pop_back将最后的一个元素移出;
}

      4.在中间添加元素

if(插入点之前的元素较少) {
    调用push_front(front())根据deque最前面的对象创建一个副本放入最前面;
    调用copy将从_M_start + 2开始到插入点位置,调用copy函数进行前移;
}
else{//插入点之后的元素较少
    调用push_back(back())将最后一个元素的副本放入末尾;
    调用copy_backward将插入点之后的元素后移;
}
最后将带插入元素放入插入点

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值