deque

      vector是单向开口的连续线性空间,deque是双向开口(在头尾分别做元素删除和插入操作)的连续线性空间

     vector与deque的区别:

      1)deque允许在头部快速进行元素插入或删除操作,而vector只允许在尾部插入元素,头部只能查看不能插入或删除

       2)deque没有容量(capacity)的概念,因为它是动态地以分段连续空间组合而成的,随时可以增加一段新空间并链接起来。不会像vector那样“因旧空间不够而新配置一个更大空间,然后复制元素,再释放旧空间”。

    deque的迭代器也是Ramdon Access Iterator,但它的复杂度和vector是不一样的。一般尽可能选vector。

    对deque进行排序操行,为了最高效率,可将deque先复制到vector中,将vector排序后(用STL sort算法),再复制到deque。

 

deque的中控器

    deque由一段一段的定量连续空间构成。一旦有必要在deque的前端或尾端增加新空间,便配置一段连续空间,串接在整个deque的头或尾部。deque的最大任务,便是在这些分段的定量连续空间上,维护其整体连续的假象,并提供随机存取的接口。

      deque采用一块所谓的map(注意,不是STL的map容器)作为主控。这里所谓map是一小块连续空间,其中每个元素(此处称为一个节点,node)都是指针,指向另一段(较大的)连续线性空间,称为缓冲区。缓冲区才是deque的储存空间主体。SGI STL 允许我们指定缓冲区大小,默认值0表示将使用512 bytes 缓冲区。

template <class T, class Alloc = alloc, size_t BUFSIZ = 0>  
class deque {  
public:              //基本类型  
    typedef T value_type;  
    typedef value_type* pointer;  
    ...  
protected:          //内部类型  
    typedef pointer* map_pointer;  
protected:  
    map_pointer map; //指向map,map是一个T**,也就是一个指针,其所指之物又是指针  
    size_type map_size; //map内可以容纳多少指针  
};  

       deque是分段连续的,但在外面它又一直维护着整体连续的假象。我们先看看deque的数据结构:

 iterator start;               // 起始缓冲区  
  iterator finish;              // 最后一个缓冲区  
  // 指向map, map是一个连续的空间, 其每个元素都是一个指向缓冲区的指针  
  // 其模型见前面的__deque_iterator  
  map_pointer map;  
  size_type map_size;   // map容量,有多少个指针  

   如下是迭代器的结构:

 T* cur;       // 此迭代器所指之缓冲区中的现行元素  
  T* first;     // 此迭代器所指之缓冲区的头  
  T* last;      // 此迭代器所指之缓冲区的尾(含备用空间)  
  map_pointer node; //指向管控中心,也就是中控器里的节点,很重要!! 

此外迭代器还得完成“整体连续”的假象,如下:

/* 
  迭代器内对各种指针运算都进行了重载, 
  所以各种指针运算如加、减、前进、后退等都不能直观视之。 
  最关键的是:一旦遇到缓冲区边缘,要特别小心, 
  视前进或后退而定,可能需要调用set_node()跳一个缓冲区 
  */  
  void set_node(map_pointer new_node)  
  {  
    node = new_node;  
    first = *new_node;  
    last = first + difference_type(buffer_size());  
  }  
};  


 self& operator++()  
  {  
    ++cur;       //切换到下一个元素  
    if (cur == last) {    //如果达到了缓冲区尾端 (“缓冲区”!!注意“last”与“fininsh”) 
      set_node(node + 1); //就切换至下一节点(也就是缓冲区)  
      cur = first;        //的第一个元素  
    }  
    return *this;  
  }  
  
  // 后缀自增  
  // 返回当前迭代器的一个副本, 并调用前缀自增运算符实现迭代器自身的自增  
  self operator++(int)  {  
    self tmp = *this;  
    ++*this;  
    return tmp;  
  }  
  
  // 前缀自减, 处理方式类似于前缀自增  
  // 如果当前迭代器指向元素是当前缓冲区的第一个元素  
  // 则将迭代器状态调整为前一个缓冲区的最后一个元素  
  self& operator--()  
  {  
    if (cur == first) {  
      set_node(node - 1);  
      cur = last;  
    }  
    --cur;  
    return *this;  
  }  
  
  self operator--(int)  
  {  
    self tmp = *this;  
    --*this;  
    return tmp;  
  }  
  
//实现随机存取,迭代器可以直接跳跃n个距离  
  self& operator+=(difference_type n)  
  {  
    difference_type offset = n + (cur - first);  
    if (offset >= 0 && offset < difference_type(buffer_size()))  
      cur += n; //目标位置在同一缓冲区内  
    else {  //目标位置不在同一缓冲区内  
      difference_type node_offset =  
        offset > 0 ? offset / difference_type(buffer_size())  
                   : -difference_type((-offset - 1) / buffer_size()) - 1;  
      set_node(node + node_offset);  //切换到正确的节点(缓冲区)  
      //切换到正确的元素  
      cur = first + (offset - node_offset * difference_type(buffer_size()));  
    }  
    return *this;  

deque 的内存管理

deque有自己专属的空间配置器,如下:

protected:                
  typedef pointer* map_pointer;  
//专属空间配置器,每次配置一个元素大小
  typedef simple_alloc<value_type, Alloc> data_allocator;  
//专属空间配置器,每次配置一个指针大小
  typedef simple_alloc<pointer, Alloc> map_allocator;

//构造函数
  deque(int n, const value_type& value)  
    : start(), finish(), map(0), map_size(0)  
  {  
    fill_initialize(n, value);  
  }  


template <class T, class Alloc, size_t BufSize>  
void deque<T, Alloc, BufSize>::fill_initialize(size_type n,  
                                               const value_type& value)  
{  
  create_map_and_nodes(n); //把deque的结构都产生并安排好  
  map_pointer cur;  
  __STL_TRY {  
      //为每个节点的缓冲区设置初值  
    for (cur = start.node; cur < finish.node; ++cur)  
      uninitialized_fill(*cur, *cur + buffer_size(), value);  
    //最后一个缓冲区的设定不太相同  
    uninitialized_fill(finish.first, finish.cur, value);  
  }  
#       ifdef __STL_USE_EXCEPTIONS  
  catch(...) {  
    for (map_pointer n = start.node; n < cur; ++n)  
      destroy(*n, *n + buffer_size());  
    destroy_map_and_nodes();  
    throw;  
  }  
#       endif /* __STL_USE_EXCEPTIONS */  
}  

// 创建内部使用的map,负责安排产生并安排好deque的结构  
template <class T, class Alloc, size_t BufSize>  
void deque<T, Alloc, BufSize>::create_map_and_nodes(size_type num_elements)  
{  
  // 需要的结点数 = (元素个数 / 每个缓冲区能容纳的元素数 + 1)  
  size_type num_nodes = num_elements / buffer_size() + 1;  
  
  // map要维护的结点, 这里最小的值为8, 见initial_map_size()  
  //最大为“所需节点数加2”,前后各预备一个,扩充时可用  
  map_size = max(initial_map_size(), num_nodes + 2);  
  //配置有map_size个节点的map  
  map = map_allocator::allocate(map_size);  
  
  // 将[nstart, nfinish)区间设置在map的中间,  
  // 这样就能保证前后增长而尽可能减少map的重新分配次数  
  map_pointer nstart = map + (map_size - num_nodes) / 2;  
  map_pointer nfinish = nstart + num_nodes - 1;  
  
  // 分配结点空间  
  map_pointer cur;  
  __STL_TRY {  
      //为map内的每个现用节点配置缓冲区。所有缓冲区加起来就是deque  
      //的可用空间(最后一个缓冲区可能留有一些富裕)  
    for (cur = nstart; cur <= nfinish; ++cur)  
      *cur = allocate_node();  
  }  
#     ifdef  __STL_USE_EXCEPTIONS  
  catch(...) {  
    for (map_pointer n = nstart; n < cur; ++n)  
      deallocate_node(*n);  
    map_allocator::deallocate(map, map_size);  
    throw;  
  }  
#     endif /* __STL_USE_EXCEPTIONS */  
  
  // 维护指针状态,为deque的两个迭代器start、end设置正确内容  
  start.set_node(nstart);  
  finish.set_node(nfinish);  
  start.cur = start.first;  
  finish.cur = finish.first + num_elements % buffer_size();  
}  

//析构函数
 ~deque()  
  {  
    destroy(start, finish);     // <stl_construct.h>  
    destroy_map_and_nodes();  
  }  

template <class T, class Alloc, size_t BufSize>  
void deque<T, Alloc, BufSize>::destroy_map_and_nodes()  
{  
  for (map_pointer cur = start.node; cur <= finish.node; ++cur)  
    deallocate_node(*cur);  
  map_allocator::deallocate(map, map_size);  
}  

deque的元素操作:

push_back(),push_front(),pop_back(),pop_front()

 void push_back(const value_type& t)  
  {  
    // STL使用前闭后开的区间, 所以如果缓冲区还有一个以上的备用空间,  
    // 则直接在finish.cur上构造对象即可, 然后更新迭代器  
    if (finish.cur != finish.last - 1) {  
      construct(finish.cur, t);  
      ++finish.cur;  
    }  
    // 容量已满就要新申请内存了  
    else  
      push_back_aux(t);  
  }  

// 仅当finish.cur == finish.last - 1才调用  
// 即最后一个缓冲区只剩一个备用空间才调用  
// 先配置一块新缓冲区,并设置新元素内容,然后更改迭代器  
template <class T, class Alloc, size_t BufSize>  
void deque<T, Alloc, BufSize>::push_back_aux(const value_type& t)  
{  
  value_type t_copy = t;  
  reserve_map_at_back(); //若符合某种条件则必须重换一个map  
  *(finish.node + 1) = allocate_node(); //配置一个新节点(缓冲区)  
  __STL_TRY {  
    construct(finish.cur, t_copy);  
    finish.set_node(finish.node + 1); //改变finish,令其指向新缓冲区  
    finish.cur = finish.first; //设定finish的状态  
  }  
  __STL_UNWIND(deallocate_node(*(finish.node + 1)));  
}    

  void push_front(const value_type& t)  
  {  
    if (start.cur != start.first) { //第一个缓冲区还有备用空间  
      construct(start.cur - 1, t);  //直接在备用空间上构造元素  
      --start.cur;  //调整第一缓冲区的使用状态  
    }   
    else  //第一缓冲区无备用空间  
      push_front_aux(t);  
  }  
  
//  start.cur == start.first.  
template <class T, class Alloc, size_t BufSize>  
void deque<T, Alloc, BufSize>::push_front_aux(const value_type& t)  
{  
  value_type t_copy = t;  
  reserve_map_at_front();  
  *(start.node - 1) = allocate_node(); //若符合某条件则必须重换一个map  
  __STL_TRY {  
    start.set_node(start.node - 1); //配置一个新缓冲区  
    start.cur = start.last - 1;  
    construct(start.cur, t_copy);  
  }  
#     ifdef __STL_USE_EXCEPTIONS  
  catch(...) {  //commit or rollback:若非全部成功,就一个不留  
    start.set_node(start.node + 1);  
    start.cur = start.first;  
    deallocate_node(*(start.node - 1));  
    throw;  
  }  
#     endif /* __STL_USE_EXCEPTIONS */  
}  

  void pop_back()  
  {  
    if (finish.cur != finish.first) {  
        //如果最后一个缓冲区有一个或多个元素  
      --finish.cur; //调整指针,相当于排除最后元素  
      destroy(finish.cur); //将最后元素析构  
    }  
    else //最后缓冲区没元素  
      pop_back_aux();  //进行缓冲区的释放  
  }  
  
  //  finish.cur == finish.first.  
template <class T, class Alloc, size_t BufSize>  
void deque<T, Alloc, BufSize>:: pop_back_aux()  
{  
  deallocate_node(finish.first); //释放最后一个缓冲区  
  finish.set_node(finish.node - 1);//调整finish的状态,指向  
  finish.cur = finish.last - 1;    //上一个缓冲区的最后一个元素  
  destroy(finish.cur);  
} 
 
  //与pop_back()类似  
  void pop_front() {  
    if (start.cur != start.last - 1)  
    {  
      destroy(start.cur);  
      ++start.cur;  
    }  
    else  
      pop_front_aux();  
  }  

  template <class T, class Alloc, size_t BufSize>  
void deque<T, Alloc, BufSize>::pop_front_aux()  
{  
  destroy(start.cur);  
  deallocate_node(start.first);  
  start.set_node(start.node + 1);  
  start.cur = start.first;  
}  

当前后端空间不足时要进行整治:

reverse_map_at_back(),reverse_map_at_front()

void reserve_map_at_back (size_type nodes_to_add = 1)  
  {  
    if (nodes_to_add + 1 > map_size - (finish.node - map))  
        //如果map尾端的节点备用空间不够  
        //符合以上条件则必须重换一个map(配置更大,拷贝原来的,释放原空间)  
      reallocate_map(nodes_to_add, false);  
  }  
  
  void reserve_map_at_front (size_type nodes_to_add = 1)  
  {  
    if (nodes_to_add > start.node - map)  
        //如果map前端的节点备用空间不够  
        //符合以上条件则必须重换一个map(配置更大,拷贝原来的,释放原空间)  
      reallocate_map(nodes_to_add, true);  
  }  

// 重新配置map, 不会对缓冲区进行操作, map维护的是指向缓冲区的指针  
template <class T, class Alloc, size_t BufSize>  
void deque<T, Alloc, BufSize>::reallocate_map(size_type nodes_to_add,  
                                              bool add_at_front)  
{  
  size_type old_num_nodes = finish.node - start.node + 1;  
  size_type new_num_nodes = old_num_nodes + nodes_to_add;  
  
  map_pointer new_nstart;  
  if (map_size > 2 * new_num_nodes) {  
    new_nstart = map + (map_size - new_num_nodes) / 2  
                     + (add_at_front ? nodes_to_add : 0);  
    if (new_nstart < start.node)  
      copy(start.node, finish.node + 1, new_nstart);  
    else  
      copy_backward(start.node, finish.node + 1, new_nstart + old_num_nodes);  
  }  
  else {  
    size_type new_map_size = map_size + max(map_size, nodes_to_add) + 2;  
  
    map_pointer new_map = map_allocator::allocate(new_map_size);  
    new_nstart = new_map + (new_map_size - new_num_nodes) / 2  
                         + (add_at_front ? nodes_to_add : 0);  
    copy(start.node, finish.node + 1, new_nstart);  
    map_allocator::deallocate(map, map_size);  
  
    map = new_map;  
    map_size = new_map_size;  
  }  
  
  start.set_node(new_nstart);  
  finish.set_node(new_nstart + old_num_nodes - 1);  
}  

clear(),erase()

//该函数清除整个deque,deque初始状态(无任何元素)时,有一个  
//缓冲区,因此clear后恢复初始,要保留一个缓冲区  
template <class T, class Alloc, size_t BufSize>  
void deque<T, Alloc, BufSize>::clear()  
{  
  // 以下针对头尾以外的每个缓冲区(它们是饱满的)  
  for (map_pointer node = start.node + 1; node < finish.node; ++node) {  
    //将缓冲区内所有元素析构   
    destroy(*node, *node + buffer_size());  
    //释放缓冲区内存  
    data_allocator::deallocate(*node, buffer_size());  
  }  
  
  // 至少有头尾两个缓冲区  
  if (start.node != finish.node) {  
    destroy(start.cur, start.last); //将头缓冲区目前的元素析构  
    destroy(finish.first, finish.cur); //将尾缓冲区目前元素析构  
    //!!!注意:以下释放尾缓冲区,头缓冲区保留  
    data_allocator::deallocate(finish.first, buffer_size());  
  }  
  // 析构所有元素, 但是不释放空间, 因为deque要满足这个前置条件  
  // 具体的细节见本文件开头'特性'  
  else  
    destroy(start.cur, finish.cur);  
  finish = start;  //调整状态  
}  

template <class T, class Alloc, size_t BufSize>  
deque<T, Alloc, BufSize>::iterator  
deque<T, Alloc, BufSize>::erase(iterator first, iterator last)  
{  
    //清楚的是整个deque,直接调用clear()  
  if (first == start && last == finish) {  
    clear();  
    return finish;  
  }  
  else {  
    difference_type n = last - first; //清除区间长度  
    difference_type elems_before = first - start; //清楚区间前方的元素  
  
    //判断清除区间前后哪个元素少  
    if (elems_before < (size() - n) / 2) { //如果前方元素少,则将前方元素  
      copy_backward(start, first, last);   //后移(覆盖清除区间)  
      iterator new_start = start + n;  //标记deque新起点  
      destroy(start, new_start); //移动完毕,将冗余元素析构  
      //将冗余缓冲区释放  
      for (map_pointer cur = start.node; cur < new_start.node; ++cur)  
        data_allocator::deallocate(*cur, buffer_size());  
      start = new_start;  //设定deque新起点  
    }  
    else { //清除区间后方元素少,向前移动后方元素(覆盖清除区)  
      copy(last, finish, first);  
      iterator new_finish = finish - n;  
      destroy(new_finish, finish);  
      for (map_pointer cur = new_finish.node + 1; cur <= finish.node; ++cur)  
        data_allocator::deallocate(*cur, buffer_size());  
      finish = new_finish;  
    }  
    return start + elems_before;  
  }  
}  

insert()

template <class T, class Alloc, size_t BufSize>  
void deque<T, Alloc, BufSize>::insert(iterator pos,  
                                      const_iterator first,  
                                      const_iterator last)  
{  
  size_type n = last - first;  
  if (pos.cur == start.cur) {  
    iterator new_start = reserve_elements_at_front(n);  
    __STL_TRY {  
      uninitialized_copy(first, last, new_start);  
      start = new_start;  
    }  
    __STL_UNWIND(destroy_nodes_at_front(new_start));  
  }  
  else if (pos.cur == finish.cur) {  
    iterator new_finish = reserve_elements_at_back(n);  
    __STL_TRY {  
      uninitialized_copy(first, last, finish);  
      finish = new_finish;  
    }  
    __STL_UNWIND(destroy_nodes_at_back(new_finish));  
  }  
  else  
    insert_aux(pos, first, last, n);  
}  


template <class T, class Alloc, size_t BufSize>  
typename deque<T, Alloc, BufSize>::iterator  
deque<T, Alloc, BufSize>::insert_aux(iterator pos, const value_type& x)  
{  
  difference_type index = pos - start; //插入点之前的元素个数  
  value_type x_copy = x;  
  
  if (index < size() / 2) {   //如果插入点前元素个数少  
    push_front(front()); //在最前端加入与第一个元素同值的元素  
    iterator front1 = start;  
    ++front1;  
    iterator front2 = front1;  
    ++front2;  
    pos = start + index;  
    iterator pos1 = pos;  
    ++pos1;  
    copy(front2, pos1, front1); //元素移动  
  }  
  else { //插入点后的元素个数较少  
    push_back(back());  //在尾部插入一个与最后一个元素同值的元素  
    iterator back1 = finish;  
    --back1;  
    iterator back2 = back1;  
    --back2;  
    pos = start + index;  
    copy_backward(pos, back2, back1); //元素移动  
  }  
  *pos = x_copy; //在插入点上设定新值  
  return pos;  
}  

 

posted on 2016-03-21 11:57  RenewDo  阅读(333)  评论(0编辑  收藏  举报

导航