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