STL源码剖析 deque双端队列 概述

  • 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)算法详解

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/446269.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

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

Stack是一种先进后出的数据结构&#xff0c;他只有一个出口stack允许 新增元素、移除元素、取得最顶端的元素&#xff0c;但是无法获得stack的内部数据&#xff0c;因此satck没有遍历行为Stack定义的完整列表 (双端队列作为Stack的底层容器) 将deque作为Stack的底部结构&#…

python怎么实现图像去噪_基于深度卷积神经网络和跳跃连接的图像去噪和超分辨...

Image Restoration Using Very Deep Convolutional Encoder-Decoder Networks with Symmetric Skip Connections作者&#xff1a;Xiao-Jiao Mao、Chunhua Shen等本文提出了一个深度的全卷积编码-解码框架来解决去噪和超分辨之类的图像修复问题。网络由多层的卷积和反卷积组成&a…

STL源码剖析 queue队列概述

queue是一种先进先出的数据结构&#xff0c;他有两个出口允许新增元素&#xff08;从最底端 加入元素&#xff09;、移除元素&#xff08;从最顶端删除元素&#xff09;&#xff0c;除了对于顶端和底端元素进行操作之外&#xff0c;没有办法可以获取queue的其他元素即queue没有…

python为什么运行不了_python为什么会环境变量设置不成功

学习python编程&#xff0c;首先要配置好环境变量。本文主要讲解python的环境变量配置&#xff0c;在不同版本下如何安装 Windows 打开Python官方下载网站 x86:表示是32位电脑 x86-64:表示是64位电脑 目前Python版本分为2.x版本和3.x版本。推荐大家使用3.x版本。 设置环境变量&…

STL 源码剖析 heap堆

heap不属于STL容器的组件&#xff0c;属于幕后角色&#xff0c;是priority_queue的助手priority_queue 允许用户以任何次序将任何元素推入容器内&#xff0c;但是取出的时候需要从优先级最高(也就是数值最高)的元素开始取&#xff0c;这种思想是基于heap的函数实现如果使用list…

java 打印星号

代码1 package lesson.l2_for; //6列4行 //****** //****** //****** //****** public class ForDemo8 {public static void main(String[] args) {for (int i1;i<4;i){for (int j 1; j <6 ; j) {System.out.print("*");}System.out.println();}} }代码2 pa…

python从小白到大牛百度云盘_Java从小白到大牛 (关东升著) 中文pdf+mobi版[36MB]

《Java从小白到大牛》是一本Java语言学习立体教程&#xff0c;读者群是零基础小白&#xff0c;通过本书的学习能够成为Java大牛。主要内容包括&#xff1a;Java语法基础、Java编码规范、数据类型、运算符、控制语句、数组、字符串、面向对象基础、继承与多态、抽象类与接口、枚…

java打印九九乘法表

代码1 package lesson.l5_loop; //九九乘法表 //1*11 //2*12 2*24 //3*13 3*26 3*39 //4*14 4*28 4*312 4*416 //5*15 5*210 5*315 5*420 5*525 //6*16 6*212 6*318 6*424 6*530 6*636 //7*17 7*214 7*321 7*428 7*535 7*642 7*749 //8*18 8*216 8*324 8*432 8*540 8*648 8*75…

STL源码剖析 priority_queue

priority_queue是一个拥有权重概念的queue&#xff0c;允许底部加入新的元素&#xff0c;头部删除旧的元素&#xff0c;以及审视元素数值的操作priority_queue带有权重的概念&#xff0c;即元素按照权重进行排列&#xff0c;而不是按照插入队列的顺序进行排序。要求权值高者在前…

python数字1 3怎么表示_Python入门篇之数字

数字类型 数字提供了标量贮存和直接访问。它是不可更改类型&#xff0c;也就是说变更数字的值会生成新的对象。当然&#xff0c;这个过程无论对程序员还是对用户都是透明的&#xff0c;并不会影响软件的开发方式。 Python 支持多种数字类型&#xff1a;整型、长整型、布尔型、双…

STL源码剖析 slist单向链表概述

概述 SGI STL的list是一个双向链表&#xff0c;单向链表是slist&#xff0c;其不在标准规格之内单向和双向链表的区别在于&#xff0c;单向链表的迭代器是单向的 Forward Iterator&#xff0c;双向链表的迭代器属于双向的Bidirectional Iterator。因此很多功能都被受限但是单向…

output怎么用_用树莓派实现室内温度监控

树莓派加上温度传感器实现室内温度监控。可用于家庭&#xff0c;轿车&#xff0c;工业&#xff0c;农业 等许多方面。可做温度预警&#xff0c;自动降温等操作。各位小伙伴可自行脑补发挥。1.硬件准备a.树莓派&#xff08;Raspberry Pi&#xff09;一个b.DS18B20温度传感器一个…

STL源码剖析 关联式容器

STL关联式容器以set(集合) 和 map(映射表)两大类&#xff0c;以及对应的衍生体构成,比如mulyiset(多键集合) multimap(多键映射表) ,容器的底层均基于红黑树RB-Tree也是一个独立的容器&#xff0c;但是不对外开放此外还提供了标准之外的关联式容器 hash table散列表&#xff0c…

STL源码剖析 关联式容器 红黑树

概念 红黑树不仅仅是一个二叉树&#xff0c;必须满足如下条件1&#xff0c;每个节点不是红色就是黑色 (深色底纹为黑色&#xff0c;浅色底纹为红色)2&#xff0c;根节点是黑色的3&#xff0c;如果节点为红&#xff0c;其子节点必须为黑色的4&#xff0c;任一节点至NULL(树的尾…

python生成的词云没有图案_还在为专栏封面发愁?我用Python写了个词云生成器!...

妈妈再也不用担心我写专栏找不到合适的封面了&#xff01;B站专栏的封面至少是我一直头疼的问题&#xff0c;每次写完文章却找不到合适的图片作为封面。 词云是一个很不错的选择&#xff0c;既美观&#xff0c;又提纲挈领。网上也有词云生成的工具&#xff0c;但大多收费/只能试…

java 1000以内的完数

题目 代码 package lesson.l6_review;public class PrefectNumber {public static void main(String[] args) {for (int i 1; i <1000 ; i) {int num0;for (int j 1; j <i-1 ; j) {if (i%j0){numj;}}if (inum){System.out.print(i"\t");}}} }

STL源码剖析 map

所有元素会根据元素的键值自动被排序 元素的类型是pair&#xff0c;同时拥有键值和实值&#xff1b;map不允许两个元素出现相同的键值pair 代码 template <class T1,class T2> struct pair{typedef T1 first_type;typedef T2 second_type;T1 first; //publicT2 secon…

java api接口怎么写_Java 如何设计 API 接口,实现统一格式返回?

来源&#xff1a;老顾聊技术前言接口交互返回格式控制层Controller美观美化优雅优化实现方案前言在移动互联网&#xff0c;分布式、微服务盛行的今天&#xff0c;现在项目绝大部分都采用的微服务框架&#xff0c;前后端分离方式&#xff0c;(题外话&#xff1a;前后端的工作职责…

java 二维数组

声明和初始化 静态初始化 // 静态初始化&#xff1a; // 一维数组int[] arr1_1 {1, 2, 4};System.out.println(Arrays.toString(arr1_1)); // 二维数组int[][] arr1_2 {{1, 2}, {4, 5}, {9, 10}};for (int[] i :arr1_2) {System.out.print(Arrays.toS…

STL源码剖析 hashtable

二叉搜索树具有对数平均时间的表现&#xff0c;但是这个需要满足的假设前提是输入的数据需要具备随机性hashtable 散列表这种结构在插入、删除、搜寻等操作层面上也具有常数平均时间的表现。而且不需要依赖元素的随机性&#xff0c;这种表现是以统计为基础的 hashtable的概述 …