STL源码剖析:【4】Sequence Containers-deque

Deque:双向容器 特性概述

  1. 双向进出,可以使用push_back,push_front/(pop)来对容器元素进行初始化
  2. 结构:超级vector - 后面将一一展开

Deque:数据结构:

  1. 首先类比vector,vector 作为自增长连续内存容器,并不是完全动态增长的,它内部依靠内存空间的重新申请扩大+ 复制 + 释放原容器内存空间,来伪装内存上的动态扩展(代价是昂贵的)
  2. Deque 采用中控【管控】中心Map(vector<Node*>) + Node(vector -缓冲区:实际存储位置)来实现。

    Class Data Member : 

    typedef T value_type;     typedef  value_type * pointer; 

    typedef simple_alloc<value_type,Alloc> data_allocator;

    typedef simple_alloc<pointer,Alloc> map_allocator;

  •          Map(管控中心表示):typedef pointer * map_point;     map_point map; size_type map_size ;
  •          迭代器:typedef _deque_iterator<T,T&,T*,Bufsiz> iterator;   iterator start,finish;

    Class Function Member: 

  •    void deque<T,Alloc,BufSize>::create_map_and_nodes(size_type num_elements) : 分别使用上面两个allocator来对空间进行分配
  •    void push_back(const value_type & t ) : if(finish.cur ! = finish.last - 1){construct(finish.cur,t); ++finish.cur;}else push_back_aux(t)  //注意进行新node的申请(push_back_aux是当前node中剩下一个可用元素空间的时候调用
  •  void deque<T,Alloc,BufSize>::push_back_aux(const value_type & t ) :reserve_map_at_back(); new_node = allocate_node();construct(finish.cur,t); finish.setNode(mew_node);finish.cur = finish.first; //得益于上一步中当当前node剩下 //一个元素的时候进行调用,所以本次申请结构简单,cur指向first就可以 
  • void push_front(const value_type & t )  : 同比类似于push_back,只不过为 -- finish.cur ,且边界条件改变,因为从后往前,开始以start.cur = start.last ,然而赋值却在start.cur -1 处调用构造函数 ,以下为源代码:if(start.cur != start.first) construct(start.cur -1 ); --start.cur; 
  • void deque<T,Alloc,BufSize>::push_front_aux(const value_type & t ) :对比以上
  • void deque<T,Alloc,BufSize>::reallocate_map(size_type nodes_to_add,bool add_at_front) :reserve_map_at_front/back都是调用这个函数,仅仅判断条件不一样(都是在判断Map Front/Back是否还有下一个可用阶段);分两个判断:第一个判断:当前使用的start-finish+1+new_added_node(包括新申请的node数量)是否到达map_size的一半?是否规范? 如果老是使用push_back必然会导致 Map后面的node*都被填满 (这个地方忘记讲了,为了平衡Deque存储效率,map并不是以map[0]作为起始,因为它是双向进出,所以以一半为开始点),这个地方会在不申请新空间的基础上,从新调整start和finish的位置,当new_start的位置小于start,此阿勇copy() ,否则采用copy_backward() (考虑到Copy时的覆盖问题);第二个判断会重新申请Map的空间,然后调整并拷贝原Map中的数据
  • void pop_back(const value_type & t ) :  类比push_back,对称:调用destroy函数释放空间,将finish.cur --, 如果说push_back需要考虑的是node扩容问题,那么pop_back需要考虑的就是缩node的问题(释放node)
  •  void pop_front(const value_type & t ) : 类比同push_front
  • vdeque<T,Alloc,BufSize>::iterator  deque<T,Alloc,BufSize>::erase (iterator first, iterator  last ) : 从前面或者后面删除都是简单的,因为不涉及到删除后的顺位问题。而连续内存容器删除是耗费极大的,虽然deque规避了在扩容时的复制开销,但是无法避免删除时的顺位复制开销。如何减少元素的移动则是至关重要的。 Deque容器,以删除区域为边界,计算删除区域前到start的元素数量,以及删除区域尾端到finish的元素数量n,考虑是前移调整start起始点,还是后端移动调整finish结束点

Deque:迭代器设计

  Struct Data Member :

  • 其中指出Deque迭代器是Random类型即:typedef random_access_iterator_tag iterator_cateory
  • T * cur ;  //当前node缓冲区中的下一个可用元素的位置
  • T * first ; // 当前node缓冲区的第一个元素的位置
  • T * last; /// 当前node缓冲区的最后一个有效元素的下一个位置(包含备用空间)
  • map_pointer node //指向管控中心当前node的首地址 

   Deque容器在整体上显示连续性,完全归功于迭代器内部对++,--,[] (下标操作符),-, + 运算符的重载,使得用户感觉不到在node缓冲区间的跳转。

 

 

发布了12 篇原创文章 · 获赞 1 · 访问量 1268

猜你喜欢

转载自blog.csdn.net/qq_34250367/article/details/105733027