《STL源码剖析》深入分析序列式容器——deque

来源:互联网 发布:js 鼠标右键事件 编辑:程序博客网 时间:2024/05/21 10:31
1、deque的优缺点
        欲抑先扬!先来看一下deque的优点:(1)头部插入删除操作,常数时间;(2)vector的假象三部曲“因空间不足a寻觅更大空间;b复制原数据;c释放源空间”在deque是不会发生的。因此也就不需要提供空间预留功能。
        deque的缺点:它的迭代器并不是普通指针,其复杂度远远超出vector,这也直接影响了各个运算层面。deque实质上由一段一段的连续空间组成,如果有必要在前端或者尾端增加新空间,便配置一段定量的连续空间串接在前端或者尾端。
既然是分段连续空间,必然有中央控制。也就是说,为了维持整体连续的假象,数据结构的设计及迭代器前进后退等都相当繁琐。下图是deque 的结构设计中map和node-buffer(节点缓冲区)的关系简图。
2、deque的迭代器
        首先,deque的迭代器必须能够指出分段连续空间(缓冲区)在哪里;其次,它必须能够判断自己是否已经处在所在缓冲区的边缘,以便前进或者后退时跳跃至上一个或者下一个缓冲区。最后,为了能够跳跃到正确的分段缓冲区,他还必须随时和主控中心联系。

T* cur;            //此迭代器所指之缓冲区中现行(current)的元素T* first;          //此迭代器所指之缓冲区中头T* last;           //此迭代器所指之缓冲区中尾(含备用空间)map_pointer node;  //指向主控中心
//跳一个缓冲区void set_node(map_pointer new_node){node = new_node;first = *new_node;last = first + dirrerence_type(buffer_size());}//迭代器直接跳跃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;}
3、deque中map的管理
      我们先来看一下map中怎么配置的:
size_type num_nodes = num_elements / buffer_size() + 1;    //需要节点数=(元素个数、每个分段节点(缓冲区)可容纳的元素个数)map_pointer nstart = map + (map_size - num_nodes) / 2;   //为了使头尾的扩充能量一样大,令nstart和nfinish指向map的最中央区段map_pointer nfinish = nstart + num_nodes - 1;
       再来看一下map的“重新整治“:
//下面两个操作判断什么时候map需要整治void reserve_map_at_back (size_type nodes_to_add = 1) {    if (nodes_to_add + 1 > map_size - (finish.node - 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的前端备用空间不足      reallocate_map(nodes_to_add, true);}//下面操作具体执行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 {    //配置一块空间,准备给新的map使用    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);        //拷贝原map内容    copy(start.node, finish.node + 1, new_nstart);    //释放原map    map_allocator::deallocate(map, map_size);    //设定新map的起始地址和大小    map = new_map;    map_size = new_map_size;  }  //重新设定迭代器start和finish  start.set_node(new_nstart);  finish.set_node(new_nstart + old_num_nodes - 1);}
4、deque的元素操作
(1)push_back() 操作


void push_back(const value_type& t){    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)));}
(2)push_front()操作

void push_front(const value_type& t) {    if (start.cur != start.first) {  //第一缓冲区尚元素备用空间      construct(start.cur - 1, t);   //直接在备用空间上构造元素      --start.cur;     //调整第一缓冲区的使用状态    }    else      push_front_aux(t);   //第一缓冲区已无元素备用空间  }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();              //调用上述的map的重新整治操作  *(start.node - 1) = allocate_node();  __STL_TRY {    start.set_node(start.node - 1);    start.cur = start.last - 1;    construct(start.cur, t_copy);  }# ifdef __STL_USE_EXCEPTIONScatch(...) {          //若非全部成功,就一个不留    start.set_node(start.node + 1);    start.cur = start.first;    deallocate_node(*(start.node - 1));    throw;  }# endif /* __STL_USE_EXCEPTIONS */} 
0 0
原创粉丝点击