deque源碼1(deque概述、deque中的控制器)html
deque源碼2(deque迭代器、deque的數據結構) 前端
deque源碼3(deque的構造與內存、ctor、push_back、push_front)node
deque源碼4(deque元素操做:pop_back、pop_front、clear、erase、insert)數據結構
deque的構造與內存函數
deque自行定義了兩個專屬的空間配置器:post
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的結構:spa
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狀態以下圖:3d
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狀態以下:code
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); }