deque源码3(deque的构造与内存、ctor、push_back、push_front)
deque源码3(deque的构造与内存、ctor、push_back、push_front)
deque源码4(deque元素操作:pop_back、pop_front、clear、erase、insert)
deque的构造与内存
deque自行定义了两个专属的空间配置器:
protected: typedef simple_alloc<value_type,Alloc> data_allocator; //专属空间配置器,每次配置一个元素大小 typedef simple_alloc<pointer,Alloc> map_allocator; //专属空间自配器,每次配置一个指针大小 deque(int n,const value_type& value): start(),finish(),map(0_,map_size(0){ fill_initialize(n,value); }
fill_initialize()负责产生并安排好deque的结构,并将元素的初值设定妥当:
template <class T,class Alloc,size_t BufSize> void deque<T.Alloc,BufSize>::fill_initialize(size_type n,const value_type& value){ create_map_and_nodes(n); //把deque的结构都产生并安排好 map_pointer cur; __STL_TRY{ //为每个节点的缓冲区设定初值 for(cur=start.node;cur<finish.node;++cur) uninitialized_fill(*cur,*cur+buffer_size(),value): uninitialized_fill(finish.first,finish.cur,value); } catch(...){ ... } }
其中create_map_and_nodes()负责产生并安排好deque的结构:
template <class T,class Alloc,size_t BufSize> void deque<T,Alloc,BufSize>::create_map_and_nodes(size_type num_elements){ //需要的节点数=(元素个数/每个缓冲区可容纳的元素个数)+1 //如果刚好除整数,会多配置一个节点 size_type num_nodes=num_elements/buffer_size()+1; //一个map要管理几个节点,最少8个,最多是"所需节点数加2" //前后各预备一个,扩充时可用 map_size=max(inital_map_size(),num_nodes+2); map=map_allocator::allocate(map_size); //配置出一个"具有map_size个节点"的map //以下令nstart和nfinish指向map所拥有之全部节点的最中央区段 //保持在最中央,可使头尾两端的扩充能量一样大,每个节点可对应一个缓冲区 map_pointer nstart=map+(map_size-num_nodes)/2; map_pointer nfinish=nstart+num_nodes-1; map_pointer cur; __STL_TRY{ //为map内的每个现用节点配置缓冲区,所有缓冲区加起来就是deque的可用空间(最后一个缓冲区可能留有一些富裕) for(cur=nstart;cur<=nfinish;++cur) *cur=allocate_node(); } catch(...){ //若成功全部执行,不成功一个都不执行 ... //为deque内的两个迭代器start和end设定正确内容 start.set_node(nstart); finish.set_node(nfinish); start.cur=start.first; finish.cur=finish.first+num_element%buffer_size();//整除时,会多配一个节点,cur指向该节点缓冲区的起始处 } }
举一个例子,代码如下,deque状态如下图:
deque<int> mydeque(20,0); for(int i=0;i<mydeque.size();i++) mydeque[i]=i; for(int i=0;i<3;i++) mydeque.push_back(i);
push_back()函数内容如下:
public: void push_back(const value& t){ if(finish.cur!=finish.last-1){ //最后缓冲区上有至少一个备用空间 construct(finish.cur,t); //直接在备用空间上构造元素 ++finish.cur; //调整最后缓冲区的使用状态 } else //最后缓冲区已无元素备用空间或者只有一个元素备用空间 push_back_aux(t); }
接着上面的例子,再在mydeque后面添加一个元素3,由于尾端只存在一个元素的备用空间,所以必须调用push_back_aux,先配置一整块的缓冲区,再添加新的元素,deque状态如下:
push_back_aux()函数内容如下:
//只有最后一个缓冲区只剩一个备用元素空间时才会被调用 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))); }
接着上面的例子,在mydeque的前端插入99,deque状态如下:
push_front()函数操作如下:
public: void push_front(const value& t){ if(satrt.cur!=start.first){ //第一缓冲区尚有备用空间 construct(start.cur-1,t); //直接在备用空间上构造元素 --start.cur; //调整第一缓冲区的使用状态 } else //第一缓冲区已无备用空间 push_front_aux(t); }
由上图可知,这里必须调用push_front_aux(),push_front_aux()函数操作如下:
//只有第一个缓冲区只剩一个备用元素空间时才会被调用 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_front(); //若符合某种条件则必须重换一个map *(start.node-1)=allocate_node(); //配置一个新的节点(缓冲区) __STL_TRY{ start.set_node(start.node-1); //改变start,令其指向新节点 start.cur=start.last-1; //设定start的状态 construct(start.cur,t_copy); //针对标的元素设值 } catch(...){ //若成功全部执行,若失败全部执行 start.set_node(start.node+1); start.cur=satrt.first; deallocate_node(*(start.node-1)); throw; } }
reserve_map_at_back、reserve_map_at_front
reserve_map_at_back()、reserve_map_at_front()这两个函数会在什么时候调用?答案是它们会在map需要重新整治的时候,也就是map的节点备用空间不足的时候。
reserve_map_at_front()函数操作如下:
void reserve_map_at_front(size_type nodes_to_add=1){ if(nodes_to_add>start.node-map) //如果map前端的节点备用空间不足,则必须重新换一个map reallocate_map(nodes_to_add,true); //配置更大的,拷贝原来的,释放原来的 }
reserve_map_at_back()函数操作如下:
void reserve_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); }
reallocate_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{ size_type new_map_size=map_size+max(map_size,node_to_add)+2; //配置一块空间,准备给新map使用 map_pointer new_map=map_allocator::allocte(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_nastart); //释放原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); }