• vector是单向开口的连续线性空间,deque是一种双向开口的连续线性空间。
  • deque可以在头尾两端分别进行元素的插入和删除操作

  • vector和deque的差异

    • 1,deque允许常数时间内对于头端元素进行插入和删除操作
    • 2,deque没有所谓容量(capacity)的概念,因为它是动态的以分段连续空间组合而成的,随时可以增加一段空间,将其衔接起来。deque不会发生vector那样的“因旧的空间不足而重新配置一段内存区间,然后进行元素的复制和旧的空间的释放”,因此deque不具备所谓的空间保留的能力,也就是reverse
  • 虽然deque也提供Random Access Iterator,但是他的迭代器不是普通的指针,其复杂度较高,如果可以使用vector就不要使用deque
  • 对于deque进行的排序操作,为了较高的效率,可以将deque先完整复制到一个vector内部,进行排序(STL sort算法) 再复制回deque
  • deque由一段一段的定量连续的空间构成。如果需要扩展空间,需要先配置一段定量的连续空间,将其串联在deque的头部或者尾部。deque的最大的任务就是在这些分段的定量的连续空间上,维护其整体连续的假象,并提供随机存取的接口,相较于vector只能向尾端成长的假象,实际需要进行“重新配置、复制、释放”的轮回操作而言,代价是需要设计 复杂的迭代器架构
  • deque使用分段连续性空间,是需要一个中央控制器进行统一调控,但是为了维持整体连续的假象,数据结构的设计和迭代器的前进和后退操作等颇为繁琐
  • deque采用一块所谓的map (这里不是指STL map容器)作为主控,这里的map是指一段连续的空间,其中每个元素(此处称为一个节点,node)都是指针,指向的是一段(较大的)连续内存空间,称其为缓冲区。
  • deque的存储空间的主体是 缓冲区
  • SGI STL允许我们指定缓冲区的大小,默认值0表示使用512bytes缓冲区
  • map本质是 T** ,即指针的指针,指向的是型别为T的一块空间

deque迭代器

  • deque是分段连续的空间。需要维持其整体连续的假象的任务是通过operator++和operator--两个运算子实现的
  • 迭代器应该具备什么结构呢?从功能点分析:1,指出分段连续空间(缓冲区)在哪里,从而判断自己是否已经处于缓冲区的边界处,一旦前进或者后退就必须跳跃至下一个或者上一个缓冲区。为了可以正确跳跃,需要随之掌控 控制中心(map)

  • 假设产生一个deque<int>令其缓冲区的大小为32,那么每个缓冲区可以容纳的元素个数是 32/sizeof(int) =8个元素。假设deque存储20个元素,需要20/8=3个缓冲区,所以map中控节点需要使用3个节点。
  • 迭代器start内的cur指针当然指向缓冲区的第一个元素,迭代器finish内的cur指针指向的是第三个缓冲区的最后元素(的下一个位置)。考虑到最后一个缓冲区尚有备用的空间,如果有新的元素插入到尾端,可以直接使用这个备用空间。
  • 阴影区域表示  还存在四个区域尚未使用

  • 对于各个指针的运算比如加、减、前进、后退都不可以直观看到。
  • 最关键的是:一旦行进的时候遇到缓冲区边缘,需要特别当心,需要视前进后退而定,可能需要使用set_node函数跳一个缓冲区
  • 双端队列的迭代器的代码
/** 如果n不为0,传回n,表示buffer size由用户自定义* 如果n为0,表示buffer size使用的是默认数值,那么*      如果sz(元素的大小,sizeof(value_type))小于512,传回 512/sz*      如果sz不小于512,传回1*/
inline std::size_t __deque_buf_size(std::size_t n,std::size_t sz){return n!=0 ? n : (sz < 512 ? std::size_t (512/sz):std::size_t(1));
}//双端队列 迭代器
template<class T,class Ref,class Ptr,std::size_t BufSiz>
struct __deque_iterator{ //未继承 std::iteratortypedef __deque_iterator<T,T&,T*,BufSiz> iterator;typedef __deque_iterator<T,const T&,const T*,BufSiz> const_iterator;static std::size_t buffer_size() {return __deque_buf_size(BufSiz,sizeof (T));}//未继承 std::iterator,所以需要自行撰写五个必要的迭代器对应的类别typedef std::random_access_iterator_tag iterator_category;//1typedef T value_type;               //2typedef Ptr pointer;                //3typedef Ref reference;              //4typedef std::size_t size_type;typedef ptrdiff_t difference_type;  //5typedef T** map_pointer;typedef __deque_iterator self;//保持和容器的联结T* cur;    //此迭代器所指向的缓冲区的现行(current)元素T* first;   //此迭代器所指之缓冲区的头T* last;   //此迭代器所指之缓冲区的尾 (含备用空间)map_pointer node;//指向管控中心public:void set_node(map_pointer new_node){node = new_node;first = *new_node;last = first + difference_type (buffer_size());}//重载各个运算子 __deque_iterator<> 成功运作的关键reference operator*() const {return *cur;}pointer operator->() const {return &(operator*());}difference_type operator-(const self& x) const{return difference_type (buffer_size()) * (node - x.node - 1) +(cur - first) + (x.last - x.cur);}//参考More Effective C++//Distinguish between prefix and postfix of increment and decrement operators//注意last指向的是最后一个节点的后面,所以先递增再判断self& operator++(){++cur;           //切换至下一个元素if (cur==last){  //如果已经到达所在缓冲区的尾端set_node(node+1);//切换至下一个节点 (亦 即缓冲区的第一个元素)cur = first;}return *this;}self& operator++(int){ //后置式 标准写法self tmp = *this;++*this;  //调用self& operator++()return tmp;}//first指向的是最后一个节点,所以需要先进行判断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;}//参考More Effective C++ item22//consider using op= instead of stand-alone opself operator+(difference_type n) const{self tmp = *this;return tmp += n; //调用operator+=}//通过使用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 {//以上调用operator* ,operator+return *(*this + n);}bool operator==(const self& x)const{ return cur == x.cur;}bool operator!=(const self& x)const{ return cur != x.cur;}bool operator< (const self& x)const{return (node == x.node) ? (cur < x.cur) : (node < x.node);}
};

deque的数据结构

  • deque除了需要维护一个先前说过的指向map的指针外,也需要维护start和finish两个迭代器,分别指向第一缓冲区的第一个元素和最后缓冲区的最后一个元素(的下一个位置)
  • 注意:还需要记住此刻的map的大小,因为一旦map所提供的节点不足,就需要重新配置更大的一块map
//双端队列
template<class T,class Alloc,std::size_t BufSiz = 0>
class deque{
public:                             //Basic typestypedef T value_type;typedef value_type* pointer;typedef value_type& reference;typedef std::size_t size_type;typedef ptrdiff_t difference_type;  public:typedef __deque_iterator<T,T&,T*,BufSiz> iterator;protected:                          //Internal typedefs//元素的指针的指针 (pointer of pointer of T)typedef pointer* map_pointer;protected:                          //Data membersmap_pointer  map;               //指向map,map是块连续空间,其内部的每个元素都是一个指针(称为节点),//指向的是一块缓冲区std::size_t map_size;           //map内可以容纳多少指针iterator start;                 //指向的是第一个节点iterator finish;                 //指向的是最后一个节点
public:                             //Basic accessorsiterator begin(){ return start; }iterator end(){ return finish; }reference operator[](size_type n){//调用__deque_iterator<>::operator[]return start[difference_type(n)];}//调用__deque_iterator<>::operator*reference front(){return *start;}reference back(){iterator tmp = finish;--tmp; //调用__deque_iterator<>::operator--return *tmp; //调用__deque_iterator<>::operator*/** 以上三行为何不使用 return *(finish-1); 替代* 书上给出的理由是:因为__deque_iterator<>没有为(finish-1)定义运算子*/}//调用__deque_iterator<>::operator-size_type size() const{return finish - start;}//不理解size_type max_size()const{return size_type (-1);}bool empty(){return finish == start;}
};

 

    //负责产生并安排好deque的结构void create_map_and_nodes(size_type num_elements){//需要节点的个数 = (元素的个数 / 每个缓冲区容纳的元素的个数) + 1;//如果刚好整除 会多为其分配一=一个节点size_type num_nodes = num_elements / buffer_size() + 1;//一个map需要管理几个节点。最少是8个,最多是 "所需要的节点数 + 2"//前后各预留一个 扩充的时候使用map_size = std::max(initial_map_size(),num_nodes + 2);//配置一个具有map_size的个节点的mapmap = map_allocator::allocate(map_size);//以下是让nstart和nfinish指向的map所拥有的全部节点的最中央区段//保持在最中央的目的是为了保证头尾两端扩充的能量一样大,每个节点对应一个缓冲区map_pointer nstart = map + (map_size - num_nodes) / 2;map_pointer nfinish = nstart + num_nodes -1;map_pointer cur;try {//为map内的每一个现用节点配置缓冲区,所有缓冲区加起来就是deque的可用空间//最后一个缓冲区可能存在一些富裕for (cur = nstart;cur<=finish;++cur) {*cur = allocate_node();}} catch (std::exception ) {//使用catch捕捉异常//"commit or rollback" 如非全部成功 就一个不要保留}//为deque内的两个迭代器start和end设定正确的内容start.set_node(nstart);finish.set_node(nfinish);start.cur = start.first; //first、cur都是public//如果刚好整除,会多分配一个节点//令cur指向这多分配的一个节点(所对映之缓冲区)的起始处finish.cur = finish.first + num_elements % buffer_size();}//push_back()void push_back(const value_type& t){if (finish.cur != finish.last - 1){//最后尚有一个以上的备用空间//直接在备用空间上进行元素的构造Chy::allocator<T>::construct(finish.cur,t);//调整最后缓冲区的使用状态++finish.cur;} else{//最后缓冲区已经 无(或者只剩下一个)元素的备用空间push_back_aux(t);}}//当尾端只剩下一个元素作为备用的空间,于是会调用push_back_aux()//先分配一整块新的缓冲区,再设置新的元素的内容,然后更改迭代器finish的状态//只有当 finish.cur == finish.last - 1 的时候才会调用void push_back_aux(const value_type& t){value_type t_copy = t;reverse_map_at_back(); //符合某种条件则必须重换一个map*(finish.node + 1) = allocate_node(); //配置一个新的节点 (缓冲区)try {Chy::allocator<T>::construct(finish.cur,t_copy);//针对标的元素设定初值finish.set_node(finish.node+1);//改变finish令其指向新的节点finish.cur = finish.first;      //设定finish的状态}catch (std::exception){Chy::allocator<T>::deallocate(*(finish.node + 1));}}void push_front(const value_type& t){if (start.cur != start.first){ //第一缓冲区尚有备用的空间Chy::allocator<T>::construct(start.cur - 1,t); //直接在备用空间上构造元素--start.cur; //调整第一缓冲区的使用状态} else{ //第一缓冲区已经无备用空间push_back_aux(t);}}//当start.cur == start.first时才会被调用//当第一个缓冲区没有任何备用元素的时候才会被调用void push_front_aux(const value_type& t){value_type t_copy = t;reverse_map_at_front(); //如果满足条件则必须要重新换一个map*(start.node - 1) = allocate_node(); //配置一个新的节点(缓冲区)try{start.set_node(start.node - 1); //改变start,另其指向新的节点start.cur = start.cur - 1; //设定start的状态Chy::allocator<T>::construct(start.cur,t_copy);}catch (std::exception){//commit or rollbackstart.set_node(start.node + 1);start.cur = start.first;Chy::allocator<T>::deallocate(*(start.node - 1));throw ;}}//整治的实际操作是通过调用 reallocate_map()函数 来实现的void 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){std::copy(start.node,finish.node+1,new_nstart);} else{std::copy_backward(start.node,finish.node+1,new_nstart + old_num_nodes);}} else{size_type new_map_size = map_size + std::max(map_size,nodes_to_add) + 2;//配置一块新的内存 给map使用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内容拷贝过来std::copy(start.node,finish.node+1,new_nstart);//释放原mapmap_allocator::deallocate(map,map_size);map = new_map;map_size = new_map_size;}//重新设定 迭代器 start 和 finishstart.set_node(new_nstart);finish.set_node(new_nstart + old_num_nodes - 1);}//reverse_map_at_front() 和 reverse_map_at_back()负责整治 mapvoid reverse_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 reverse_map_at_front(size_type nodes_to_add = 1){if (nodes_to_add > start.node - map){//如果map的前端节点备用空间不足//符合上述条件则必须重新替换一个map (配置更大的内存,拷贝先前的旧的区间,释放先前的旧的空间)reallocate_map(nodes_to_add, true);}}

改编版 代码

#include <iostream>template<class T,class Alloc>
class simple_alloc{
public:static T* allocate(std::size_t n){return 0==n?0:(T*)Alloc::allocate(n * sizeof(T));}static T* allocate(void){return (T*)Alloc::allocate(sizeof (T));}static void deallocate(T* p,size_t n){if (n!=0){Alloc::deallocate(p,n * sizeof(T));}}static void deallocate(T* p){Alloc::deallocate(p,sizeof(T));}
};namespace Chy{template <class T>inline T* _allocate(ptrdiff_t size,T*){std::set_new_handler(0);T* tmp = (T*)(::operator new((std::size_t)(size * sizeof (T))));if (tmp == 0){std::cerr << "out of memory" << std::endl;exit(1);}return tmp;}template<class T>inline void _deallocate(T* buffer){::operator delete (buffer);}template<class T1,class T2>inline void _construct(T1 *p,const T2& value){new(p) T1 (value);  //没看懂}template <class T>inline void _destroy(T* ptr){ptr->~T();}template <class T>class allocator{public:typedef T           value_type;typedef T*          pointer;typedef const T*    const_pointer;typedef T&          reference;typedef const T&    const_reference;typedef std::size_t size_type;typedef ptrdiff_t   difference_type;template<class U>struct rebind{typedef allocator<U>other;};pointer allocate(size_type n,const void * hint = 0){return _allocate((difference_type)n,(pointer)0);}void deallocate(pointer p,size_type n){_deallocate(p);}void construct(pointer p,const T& value){_construct(p,value);}void destroy(pointer p){_destroy(p);}pointer address(reference x){return (pointer)&x;}const_pointer const_address(const_reference x){return (const_pointer)&x;}size_type max_size()const{return size_type(UINT_MAX/sizeof (T));}};
}//如果是copy construction 等同于assignment而且destructor 是 trivial以下就会有效
//如果是POD型别 执行的流程就会跳转到以下函数,这个是通过function template的参数推导机制得到的
template<class ForwardIterator,class Size,class T>
inline ForwardIterator __uninitizlized_fill_n_aux(ForwardIterator first,Size n,const T&x){return fill_n(first,n,x); //交给高阶函数执行
}struct __true_type{};
struct __false_type{};template<class T>
struct __type_traits {typedef __true_type this_dummy_member_must_be_first;typedef __false_type has_trivial_default_constructor;typedef __false_type has_trivial_copy_constructor;typedef __false_type has_trivial_assignment_constructor;typedef __false_type has_trivial_destructor;typedef __false_type is_POD_type;
};//函数的逻辑是
//首先萃取出 迭代器first的value type,然后判断这个型别是否是POD类型
template<class ForwardIterator,class Size,class T,class T1>
inline ForwardIterator __uninitizlized_fill_n(ForwardIterator first,Size n,const T&x,T1*){//以下使用的是__type_traits<T1>::is_POD_type is _PODtypedef typename __type_traits<T1>::is_POD_type is_POD;return __uninitizlized_fill_n_aux(first,n,x,is_POD());
}//如果是copy construction 等同于assignment而且destructor 是 trivial以下就会有效
//如果是POD型别 执行的流程就会跳转到以下函数,这个是通过function template的参数推导机制得到的
template <class ForwardIterator,class T>
inline void __uninitialized_fill_aux(ForwardIterator first,ForwardIterator last,const T& x,__true_type){fill(first,last,x);//调用STL算法 fill()
}
//如果不是POD型别 执行的流程就会转向以下函数  这个是通过function template的参数推导机制得到的
template <class ForwardIterator,class T>
inline void __uninitialized_fill_aux(ForwardIterator first,ForwardIterator last,const T& x,__false_type){ForwardIterator cur = first;//为了简化 省略了异常处理for(;cur != last;++cur){Chy::_construct(&*cur,x);}
}template<class ForwardIterator,class T,class T1>
inline void __uninitialized_fill(ForwardIterator first,ForwardIterator last,const T&x,T1*){typedef typename __type_traits<T1>::is_POD_type is_POD;__uninitialized_fill_aux(first,last,x,is_POD());
}/** 迭代器first指向输出端 (欲初始化空间) 起始处* 迭代器last指向的输出端 (欲初始化空间) 结尾处 前闭后开区间* x 表示初始数值*/
template<class ForwardIterator,class T>
ForwardIterator uninitialized_fill(ForwardIterator first,ForwardIterator last,const T&x){__uninitialized_fill(first,last,x,value_type(first));
}/** 如果n不为0,传回n,表示buffer size由用户自定义* 如果n为0,表示buffer size使用的是默认数值,那么*      如果sz(元素的大小,sizeof(value_type))小于512,传回 512/sz*      如果sz不小于512,传回1*/
inline std::size_t __deque_buf_size(std::size_t n,std::size_t sz){return n!=0 ? n : (sz < 512 ? std::size_t (512/sz):std::size_t(1));
}//双端队列 迭代器
template<class T,class Ref,class Ptr,std::size_t BufSiz>
struct __deque_iterator{ //未继承 std::iteratortypedef __deque_iterator<T,T&,T*,BufSiz> iterator;typedef __deque_iterator<T,const T&,const T*,BufSiz> const_iterator;static std::size_t buffer_size() {return __deque_buf_size(BufSiz,sizeof (T));}//未继承 std::iterator,所以需要自行撰写五个必要的迭代器对应的类别typedef std::random_access_iterator_tag iterator_category;//1typedef T value_type;               //2typedef Ptr pointer;                //3typedef Ref reference;              //4typedef std::size_t size_type;typedef ptrdiff_t difference_type;  //5typedef T** map_pointer;typedef __deque_iterator self;//保持和容器的联结T* cur;    //此迭代器所指向的缓冲区的现行(current)元素T* first;   //此迭代器所指之缓冲区的头T* last;   //此迭代器所指之缓冲区的尾 (含备用空间)map_pointer node;//指向管控中心public:void set_node(map_pointer new_node){node = new_node;first = *new_node;last = first + difference_type (buffer_size());}//重载各个运算子 __deque_iterator<> 成功运作的关键reference operator*() const {return *cur;}pointer operator->() const {return &(operator*());}difference_type operator-(const self& x) const{return difference_type (buffer_size()) * (node - x.node - 1) +(cur - first) + (x.last - x.cur);}//参考More Effective C++//Distinguish between prefix and postfix of increment and decrement operators//注意last指向的是最后一个节点的后面,所以先递增再判断self& operator++(){++cur;           //切换至下一个元素if (cur==last){  //如果已经到达所在缓冲区的尾端set_node(node+1);//切换至下一个节点 (亦 即缓冲区的第一个元素)cur = first;}return *this;}self& operator++(int){ //后置式 标准写法self tmp = *this;++*this;  //调用self& operator++()return tmp;}//first指向的是最后一个节点,所以需要先进行判断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;}//参考More Effective C++ item22//consider using op= instead of stand-alone opself operator+(difference_type n) const{self tmp = *this;return tmp += n; //调用operator+=}//通过使用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 {//以上调用operator* ,operator+return *(*this + n);}bool operator==(const self& x)const{ return cur == x.cur;}bool operator!=(const self& x)const{ return cur != x.cur;}bool operator< (const self& x)const{return (node == x.node) ? (cur < x.cur) : (node < x.node);}
};//双端队列
template<class T,class Alloc,std::size_t BufSiz = 0>
class deque{
public:                             //Basic typestypedef T value_type;typedef value_type* pointer;typedef value_type& reference;typedef std::size_t size_type;typedef ptrdiff_t difference_type;public:typedef __deque_iterator<T,T&,T*,BufSiz> iterator;protected:                          //Internal typedefs//元素的指针的指针 (pointer of pointer of T)typedef pointer* map_pointer;protected:                          //Data membersmap_pointer  map;               //指向map,map是块连续空间,其内部的每个元素都是一个指针(称为节点),//指向的是一块缓冲区std::size_t map_size;           //map内可以容纳多少指针iterator start;                 //指向的是第一个节点iterator finish;                 //指向的是最后一个节点public:                             //Basic accessorsiterator begin(){ return start; }iterator end(){ return finish; }reference operator[](size_type n){//调用__deque_iterator<>::operator[]return start[difference_type(n)];}//调用__deque_iterator<>::operator*reference front(){return *start;}reference back(){iterator tmp = finish;--tmp; //调用__deque_iterator<>::operator--return *tmp; //调用__deque_iterator<>::operator*/** 以上三行为何不使用 return *(finish-1); 替代* 书上给出的理由是:因为__deque_iterator<>没有为(finish-1)定义运算子*/}//调用__deque_iterator<>::operator-size_type size() const{return finish - start;}//不理解size_type max_size()const{return size_type (-1);}bool empty(){return finish == start;}protected:                          //Internal typedef//专属配置器  每次配置一个元素的大小typedef simple_alloc<value_type,Alloc>data_allocator;//专属配置器  每次配置一个指针大小typedef simple_alloc<pointer,Alloc>map_allocator;static size_type initial_map_size() { return 8; }static size_type buffer_size() {//返回return __deque_buf_size(BufSiz, sizeof(value_type));}void allocate_node() { return data_allocator::allocate(buffer_size()); }void deallocate_node() {return data_allocator::deallocate(buffer_size());}//构造器deque(int n,const value_type& value):start(),finish(),map(0),map_size(0){fill_initialize(n,value);}
protected://负责产生并安排好deque的结构,并设定元素数值void fill_initialize(size_type n,const value_type& value){//把deque的结构转备好create_map_and_nodes(n);map_pointer cur;try {//为每个节点的缓冲区设定初始值for (cur = start.node;cur < finish.node;++cur) {uninitialized_fill(*cur,*cur + buffer_size(),value);}//最后一个节点的设定稍微有些不同(因为尾端可能有备用空间,不必为其设定初值)uninitialized_fill(finish.first,finish,cur,value);}catch (std::exception) {}}//负责产生并安排好deque的结构void create_map_and_nodes(size_type num_elements){//需要节点的个数 = (元素的个数 / 每个缓冲区容纳的元素的个数) + 1;//如果刚好整除 会多为其分配一=一个节点size_type num_nodes = num_elements / buffer_size() + 1;//一个map需要管理几个节点。最少是8个,最多是 "所需要的节点数 + 2"//前后各预留一个 扩充的时候使用map_size = std::max(initial_map_size(),num_nodes + 2);//配置一个具有map_size的个节点的mapmap = map_allocator::allocate(map_size);//以下是让nstart和nfinish指向的map所拥有的全部节点的最中央区段//保持在最中央的目的是为了保证头尾两端扩充的能量一样大,每个节点对应一个缓冲区map_pointer nstart = map + (map_size - num_nodes) / 2;map_pointer nfinish = nstart + num_nodes -1;map_pointer cur;try {//为map内的每一个现用节点配置缓冲区,所有缓冲区加起来就是deque的可用空间//最后一个缓冲区可能存在一些富裕for (cur = nstart;cur<=finish;++cur) {*cur = allocate_node();}} catch (std::exception ) {//使用catch捕捉异常//"commit or rollback" 如非全部成功 就一个不要保留}//为deque内的两个迭代器start和end设定正确的内容start.set_node(nstart);finish.set_node(nfinish);start.cur = start.first; //first、cur都是public//如果刚好整除,会多分配一个节点//令cur指向这多分配的一个节点(所对映之缓冲区)的起始处finish.cur = finish.first + num_elements % buffer_size();}//push_back()void push_back(const value_type& t){if (finish.cur != finish.last - 1){//最后尚有一个以上的备用空间//直接在备用空间上进行元素的构造Chy::allocator<T>::construct(finish.cur,t);//调整最后缓冲区的使用状态++finish.cur;} else{//最后缓冲区已经 无(或者只剩下一个)元素的备用空间push_back_aux(t);}}//当尾端只剩下一个元素作为备用的空间,于是会调用push_back_aux()//先分配一整块新的缓冲区,再设置新的元素的内容,然后更改迭代器finish的状态//只有当 finish.cur == finish.last - 1 的时候才会调用void push_back_aux(const value_type& t){value_type t_copy = t;reverse_map_at_back(); //符合某种条件则必须重换一个map*(finish.node + 1) = allocate_node(); //配置一个新的节点 (缓冲区)try {Chy::allocator<T>::construct(finish.cur,t_copy);//针对标的元素设定初值finish.set_node(finish.node+1);//改变finish令其指向新的节点finish.cur = finish.first;      //设定finish的状态}catch (std::exception){deallocate_node(*(finish.node + 1));}}void push_front(const value_type& t){if (start.cur != start.first){ //第一缓冲区尚有备用的空间Chy::allocator<T>::construct(start.cur - 1,t); //直接在备用空间上构造元素--start.cur; //调整第一缓冲区的使用状态} else{ //第一缓冲区已经无备用空间push_back_aux(t);}}//当start.cur == start.first时才会被调用//当第一个缓冲区没有任何备用元素的时候才会被调用void push_front_aux(const value_type& t){value_type t_copy = t;reverse_map_at_front(); //如果满足条件则必须要重新换一个map*(start.node - 1) = allocate_node(); //配置一个新的节点(缓冲区)try{start.set_node(start.node - 1); //改变start,另其指向新的节点start.cur = start.cur - 1; //设定start的状态Chy::allocator<T>::construct(start.cur,t_copy);}catch (std::exception){//commit or rollbackstart.set_node(start.node + 1);start.cur = start.first;deallocate_node(*(start.node - 1));throw ;}}//整治的实际操作是通过调用 reallocate_map()函数 来实现的void 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){std::copy(start.node,finish.node+1,new_nstart);} else{std::copy_backward(start.node,finish.node+1,new_nstart + old_num_nodes);}} else{size_type new_map_size = map_size + std::max(map_size,nodes_to_add) + 2;//配置一块新的内存 给map使用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内容拷贝过来std::copy(start.node,finish.node+1,new_nstart);//释放原mapmap_allocator::deallocate(map,map_size);map = new_map;map_size = new_map_size;}//重新设定 迭代器 start 和 finishstart.set_node(new_nstart);finish.set_node(new_nstart + old_num_nodes - 1);}//reverse_map_at_front() 和 reverse_map_at_back()负责整治 mapvoid reverse_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 reverse_map_at_front(size_type nodes_to_add = 1){if (nodes_to_add > start.node - map){//如果map的前端节点备用空间不足//符合上述条件则必须重新替换一个map (配置更大的内存,拷贝先前的旧的区间,释放先前的旧的空间)reallocate_map(nodes_to_add, true);}}void pop_back(){if (finish.cur != finish.first){//最后一个缓冲区存在一个 或者 更多的元素--finish.cur; //调整指针,相当于排除了最后的元素Chy::allocator<T>::destroy(finish.cur); //析构最后的元素} else{pop_back_aux();}}//finish.cur == finish.firstvoid pop_back_aux(){deallocate_node(finish.first); //释放最后一个缓冲区finish.set_node(finish.node - 1);//调整finish的状态finish.cur = finish.last - 1;//将finish的cur指针指向最后一个元素Chy::allocator<T>::destroy(finish.cur);//进行元素的析构}void pop_front(){if (start != start.last - 1){//第一缓冲区有一个或者多个元素Chy::allocator<T>::destroy(start.cur);//析构当前的元素++start.cur; //调整指针,相当于排除了第一个元素} else{pop_back_aux(); //这里将进行缓冲区的释放工作}}//当start.cur == start.last-1被调用void pop_front_aux(){Chy::allocator<T>::destroy(start.cur); //将第一缓冲区仅剩的一个元素析构deallocate_node(start.first); //释放第一个缓冲区start.set_node(start.node + 1);//调整start的状态start.cur = start.first; //将start指向缓冲区的第一个元素}//清除整个deque//需要注意的是 deque的最初状态(无任何元素的时候) 保证有一个缓冲区void clear(){//针对头尾指针以外的每一个缓冲区,都应该是饱满的for(map_pointer node = start.node + 1;node < finish.node - 1;++node){//将缓冲区内的所有元素全部析构//调用destroy()的第二版本Chy::allocator<T>::deallocate(*node,buffer_size());}if (start.node != finish.node){//至少有头尾两个缓冲区Chy::allocator<T>::destroy(start.cur,start.last); //将头缓冲区目前所有的元素析构Chy::allocator<T>::destroy(finish.first,finish.cur); //将尾缓冲区目前所有的元素析构//释放尾缓冲区的的物理内存,但是保留头部缓冲区data_allocator::deallocate(finish.first,buffer_size());} else{//只有一个缓冲区//将唯一缓冲区内的所有元素析构//注意:并不释放缓冲区的空间,将这唯一的缓冲区保留Chy::allocator<T>::destroy(start.cur,finish.cur);}//调整状态finish = start;}//使用erase消除特定位置上的元素//消除pos指向的元素,pos为清除点iterator erase(iterator pos){iterator next = pos;++next;difference_type index = pos - start; //清除点之前的元素的个数if(index < (size() >> 1)){ //如果清除点之前的元素比较少std::copy_backward(start,pos,next);//移动清除点之前的元素pop_front(); //移动完毕,将最前一个元素删除} else{//清除点之后的元素较少std::copy(next,finish,pos);//移动清除点之后的元素pop_back();//移动完毕 将最后一个冗余的元素删除}return start+index;}//使用erase删除[first,last)区间内的所有元素// 也就是删除指定范围内的一段元素iterator erase(iterator first,iterator last){if (first == start && last == finish){//如果删除的是整段内存空间,直接调用clear()clear();return finish;} else{difference_type n = last - first;        //清除区间的长度difference_type elements_before = first - start; //清除区间前方的元素个数if (elements_before < (size() - n)/2){  //如果前方的元素比较少//copy_backward和copy函数类似,只是逆向对元素进行复制std::copy_backward(start,first,last); //向后移动前方的元素(覆盖清除区间)iterator new_start = start + n; //标记deque的新的起点Chy::allocator<T>::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{//如果删除区间的后方的元素比较少std::copy(last,finish,first); //向前移动后方的元素 (覆盖清除区间)iterator new_finish = finish - n;  //标记finish的新的尾巴Chy::allocator<T>::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 + elements_before;}}//在某个点之前插入一个元素,并设定其值iterator insert(iterator position,const value_type& x){if (position.cur == start.cur){ //如果插入点是deque的最前端push_front(x);return start;}else if(position.cur == finish.cur){ //如果插入点是deque的最后端pop_back(x);iterator tmp = finish;--tmp;return tmp;} else{return insert_aux(position,x);}}iterator 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;std::copy(front2,pos1,front1);//元素的移动} else{push_back(back());iterator back1 = finish;--back1;iterator back2 = back1;--back2;pos = start + index;std::copy_backward(pos,back2,back1);}//在插入点上设定新值*pos = x_copy;return pos;}};

参考链接

  • STL之deque实现详解_一个菜鸟的博客-CSDN博客_deque
  • C++ copy_backward(STL copy_backward)算法详解

STL源码剖析 deque双端队列 概述相关推荐

  1. STL源码剖析---deque

    一.deque的中控器       deque是连续空间(至少逻辑上看来如此),连续线性空间总令我们联想到array或vector.array无法成长,vector虽可成长,却只能向尾端成长,而且其所 ...

  2. STL源码剖析 Stack栈 queue队列

     随机迭代器用于随机数据访问,所以栈stack不具备此功能

  3. STL源码剖析 stack 栈 概述->(使用deque双端队列 / list链表)作为stack的底层容器

    Stack是一种先进后出的数据结构,他只有一个出口 stack允许 新增元素.移除元素.取得最顶端的元素,但是无法获得stack的内部数据,因此satck没有遍历行为 Stack定义的完整列表 (双端 ...

  4. 6-5-2:STL之stack和queue——双端队列deque

    文章目录 双端队列-deque 双端队列-deque deque是一种双向开口的连续线性空间.所谓双向开口,意思就是可以在头尾两端分别进行元素的插入和删除操作 deque的出现是为了解决融合vecto ...

  5. C++ STL源码剖析 笔记

    写在前面 记录一下<C++ STL源码剖析>中的要点. 一.STL六大组件 容器(container): 各种数据结构,用于存放数据: class template 类泛型: 如vecto ...

  6. STL源码剖析学习七:stack和queue

    STL源码剖析学习七:stack和queue stack是一种先进后出的数据结构,只有一个出口. 允许新增.删除.获取最顶端的元素,没有任何办法可以存取其他元素,不允许有遍历行为. 缺省情况下用deq ...

  7. 《STL源码剖析》相关面试题总结

    一.STL简介 STL提供六大组件,彼此可以组合套用: 容器 容器就是各种数据结构,我就不多说,看看下面这张图回忆一下就好了,从实现角度看,STL容器是一种class template. 算法 各种常 ...

  8. STL(C++标准库,体系结构及其内核分析)(STL源码剖析)(更新完毕)

    文章目录 介绍 Level 0:使用C++标准库 0 STL六大部件 0.1 六大部件之间的关系 0.2 复杂度 0.3 容器是前闭后开(左闭右开)区间 1 容器的结构与分类 1.1 使用容器Arra ...

  9. STL源码剖析---红黑树原理详解下

    转载请标明出处,原文地址:http://blog.csdn.net/hackbuteer1/article/details/7760584       算法导论书上给出的红黑树的性质如下,跟STL源码 ...

最新文章

  1. 26岁应届博士被聘985博导!入职半年实现学院顶会论文零的突破
  2. 如何通俗地讲解对偶问题?尤其是拉格朗日对偶lagrangian duality?
  3. python语言程序设计2019版第二章课后答案-《python语言程序设计》_第二章编程题...
  4. 浏览器控件JxBrowser 6.18发布 | 极大增强了安全性
  5. 深刻理解Python中的元类(metaclass)以及元类实现单例模式
  6. CGI方式获取RTX中用户的电话和邮箱
  7. dagger2 依赖注入
  8. 广播等风暴的解决办法
  9. seaborn 画堆叠柱状图_Seaborn-基于matplotlib的更强力制图库
  10. 基于Xml 的IOC 容器-分配路径处理策略
  11. 【数论】疯狂 LCM(P1891)
  12. oracle数据库从AIX环境expdp迁移到linux环境(sec_case_sensitive_logon=true导致连接报错ORA-01017)
  13. 解决Hash冲突的两种策略
  14. java opencv 之车辆识别
  15. 元素周期表排列的规律_元素周期表中的几个规律
  16. 学生信息管理系统java_学生信息管理系统java课程设计(含源代码)
  17. 手机上将mp4转换成amv_如何在Linux上将tiff图像从RGB颜色转换为CMYK颜色?
  18. python 期货程序化_文华财经程序化以外,Python量化是更好的选择
  19. 视频:这不是科幻,厉害了,用人工智能修长城
  20. 【渝粤题库】广东开放大学 高级商务办公软件应用 形成性考核

热门文章

  1. 【转】深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第一节 理解堆与栈
  2. ASP.NET MVC 入门2、项目的目录结构与核心的DLL
  3. 【转】SharePoint 2013 开发——开发并部署webpart
  4. 一步步编写操作系统 26 打开A20地址线
  5. 设计模式(一)预备知识
  6. 记录一个JS异常Uncaught NotFoundError
  7. Angular使用Console.log()打印出来的数据没问题,点击详情后数据变了
  8. Python3 爬虫学习笔记 C12【验证码对抗系列 — 图形验证码】
  9. CCIE-LAB-第十二篇-EIGRP+EIGRP末节区域+leak map+分发列表
  10. 【POJ - 2486】Apple Tree (树形背包,dp)