deque原始碼2(deque迭代器、deque的資料結構)
阿新 • • 發佈:2019-01-04
deque的迭代器
deque是分段連續空間,維持其"整體連續"的假象任務,落在了迭代器的operator++和operator--兩個運運算元身上。
對於operator:1、必須能夠指出分段連續空間(即緩衝區)在哪裡
2、必須能夠判斷自己是否已經處於其所在緩衝區的邊緣,在跳躍時,必須掌握控制中心。
如下圖:
template <class T,class Ref,class Ptr,size_t Bufsize> struct __deque_iterator{ //為繼承 std::iterator typedef __deque_iterator<T,T&,T*,Bufsize> iterator; typedef __deque_iterator
<T,const T&,const T*,Bufsize> const_iterator; static size_t buffer_size(){return __deque_buf_size(Bufsize,sizeof(T));} //未繼承std::iterator,所以必須自行撰寫五個必要的迭代器相應型別 typedef random_access_iterator_tag iterator_category; //1 typedef T value_type; //2 typedef Ptr pointer; //3 typedef Ref renference; //4 typedef size_t size_type; typedef ptrdiff_t difference_type; //5 typedef T** map_pointer; typedef __deque_iterator self; //保持與容器的聯結 T* cur; //此迭代器所指之緩衝區中的現行(current)元素 T* first; //此迭代器所指之緩衝區中的頭 T* last; //此迭代器所指之緩衝區中的尾(含備用空間) map_pointer node; //指向管控中心 ... inline size_t __deque_buf_size(size_t n,size_t sz){return n!=0? n:(sz<512? size_t(512/sz):size_t(1)); } /* n!=0,返回n,表示buffer_size由使用者自定義 n=0,表示buffer_size使用預設值,那麼: sz<512,傳回512/sz; sz>=512,傳回1 */ };例如:產生一個deque<int>,令緩衝區大小為32,於是每個緩衝區可以容納32/sizeof(int)=8個元素,經過增刪操作,deque中包含20個元素,deque情況如下圖:
start和finish分別指向deque的第一個緩衝區和最後一個緩衝區,20/8=3,所以map擁有3個節點,且最後一個緩衝區還有插入元素的空間。
用於迭代器內對各種指標運算都進行過載操作,所以各種運算演算法都比較麻煩,特別是對於在緩衝區邊緣的元素操作都需要呼叫set_node操作,來跳一個緩衝區。程式碼如下:
void set_node(map_pointer new_node){ node=new_node; first=*new_node; last=first+difference_type(buffer_size()); }
過載運算子如下:
renference 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); } self& operator++(){ ++cur; //切換下一個元素 if(cur==last){ //如果已達到所在緩衝區的尾端 set_node(node+1); //利用set_node方法切換到下一個緩衝區 cur=first; } return *this; } self operator++(int){ self temp=*this; ++*this; //呼叫operator++ return temp; } self& operator--(){ if(cur==first){ //如果達到緩衝區的頭部 set_node(node-1); //利用set_node方法切換到上一個緩衝區 cur=first; } --cur; return *this; } self operator--(int){ self temp=*this; --*this; //呼叫operator-- return temp; } self& operator+=(difference_type n){ //實現隨機存取、迭代器可以直接跳躍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; } self operator+(difference_type n) const{ self temp=*this; return temp+=n; //呼叫operator+= } self& operator-=(difference_type n){ return *this+=-n; } self operator-(difference_type n) const{ self temp=*this; return temp-=n; //呼叫operator-= } //隨機存取第n個元素 reference operator[](difference_type n)const {return *(*this+n);} bool operator==(const self& x)const{return cur==x.cur;} bool operator!=(const self& x)const{return !(*this==x);} bool operator<(const self& x)const{ return (node==x.node)?(cur<x.cur):(node<x.node); }
deque的資料結構
deque除了維護上文map的指標外,還要維護start,finish兩個迭代器(上圖2可見),分別指向第一個緩衝區的第一個元素和最後一個緩衝區的最後一個元素的下一個位置(可能還有備用空間),此外,它當然也必須記住目前的map大小,因為一旦map所提供的節點不足,就必須重新配置更大的一塊map。
template <class T,class Alloc=alloc,size_t BufSiz=0> class deque{ public: typedef T value_type; typedef value_type* pointer; typedef size_t size_type; public: typedef __deque_iterator<T,T&,T*,BufSiz> iterator; protected: typedef pointer* map_pointer;//元素的指標的指標 protected: iterator start; //表示第一個節點 iterator finish; //表示最後一個節點 map_pointer map; //指向map,map是塊連續空間,其每個元素都是指標,指向一個節點 size_type map_size; //map內有多個指標 ... public: iterator begin(){return start;} iterator end(){return finish;} reference operator[](size_type n){ return start[difference_type(n)]; //呼叫operator[] } reference front(){return *start;} //呼叫operator* reference back(){ iterator temp=finish; --temp; //呼叫operator-- return *temp; //呼叫operator* } size_type size() const{return finish-start;} //呼叫operator- size_type max_size() const{return size_type(-1);} bool empty() const{return finish==start;} };