天天看點

STL源碼筆記之序列式容器

      所謂序列式容器,其中的元素都可序,但未必有序。 序列式容器包括array(内建)、vector、heap、priority_queue、list、slist、deque、stack(配接器)和queue(配接器)。

      容器中大量應用前面用于構造的construct和用于析構的distroy以及uninitialized_copy()、uninitialized_fill()和uninitialized_fill_n()。這些函數面對的是未初始化的記憶體,它們将記憶體的配置和對象的構造分離。在構造對象的時候對其型别采用判斷,然後根據型别調用不同的函數以達到最大化效率。

vector

      vector的資料以及操作方式與array非常相似,兩者的唯一差别在于空間的運用的靈活性。array是靜态空間,一旦配置了就不能改變。而vector是動态空間,随着元素的加入,它的内部機制會自動擴充空間以容納新元素。vector的實作技術,關鍵在于其對大小的控制以及重新配置的資料移動效率。一旦vector的舊有空間滿載,元素的空間會成倍的增長。

1.vector提供的接口

       vector提供的接口:包括得到vector的屬性接口、vector的操作接口以及構造函數:

     (1)構造函數:vector()、vector(size_type n、const T& value)、vector(size_type n);

     (2)屬性函數:begin、end、size、capacity、empty、operator[]、front和back

     (3)操作函數:push_back()、pop_back()、erase()、resize()、clear()。

注:vector沒有pop_front和push_front操作。

注:pop_back傳回的值為void類型,不是删除元素的值。

注:vector(size_type n、const T& value)這種同時初始化n個元素且初始值為T,隻适用于順序容器。

2.vector的資料結構

       vector采用的資料結構非常簡單:線性連續空間,它以兩個疊代器start和finish分别指向配置得來的連續空間中目前已被使用的範圍,并以疊代器end_of_storage指向整塊連續空間的尾端。vector采用的空間配置器為alloc。

STL源碼筆記之序列式容器
template<class T,class Alloc = alloc>
class vector{
public:
    typedef T value_type;
    typedef value_type* pointer;
    typedef value_type* iterator;//疊代器類型就是元素的指針
    typedef value_type& reference;
    typedef size_t size_type;
    typedef ptrdiff_t difference_type;
    ....
};
           

注:容量的擴張必須經曆:“重新配置、元素移動、釋放原空間“等過程。

list

     相對于vector的連續空間,list就顯得複雜許多,它的好處是每次插入或删除一個元素,就配置或釋放一個元素。是以對于空間的運用絕對精确,一點也不浪費。而且對于任何位置的元素插入或删除,list永遠是常數。

1.list提供的接口

     list提供的接口:包括得到list的屬性接口、list的操作接口以及構造函數:

   (1)構造函數:list()、list(size_type n、const T& value)、list(size_type n)。

   (2)屬性函數:begin、end、empty、size、front和back。

   (3)操作函數:push_back()、pop_back()、push_front、pop_front、erase()、resize()、clear()、unique、splice、merge、reverse、sort、insert。

//pop_front和front函數常常連用
while(!list.empty()){
    process(list.front());
    list.pop_front();
}
           

注:push_front、pop_front,隻有list和deque才有此接口

2.list的資料結構

     list本身和list的節點是不同的,下面是list節點:

STL源碼筆記之序列式容器
template<class T>
struct __list_node{
    typedef void* void_pointer;
    void_pointer prev;
    void_pointer next;
    T data;
};
           

       list的疊代器設計:

template<class T,class Ref,class Ptr>
struct __list_iterator{
    typedef __list_iterato<T,T&,T*>  iterator;
    typedef __list_iterato<T,Ref,Ptr>  self;
    typedef bidirectional_iterator_tag iterator_category;//雙向疊代器
    tyepdef  __list_node<T>* link_type;
    link_type node;//包含了一個指向__list_node節點
    .....
};
           

     list不僅是一個雙連結清單,還是一個環形的雙連結清單,是以它隻需要一個指針,便可以完整表現整個連結清單。這邊主要考慮的問題是雙連結清單的一些操作,包括删除、插入等操作。具體結構如下:

STL源碼筆記之序列式容器
template<class T,class Alloc = alloc>
class list{
protected:
    typedef __list_node<T> list_node;
public:
    typedef list_node* link_type;
    typedef __list_iterator<T,T&,T*> iterator;//包含的疊代器
protected:
    link_type node;//包含了一個指向__list_node節點
    .....
};

           

Deque

       vector是單向開口的連續線性空間,deque是一種雙向開口的連續線性空間。所謂雙向開口,就是它的頭尾兩端都可以插入元素。deque和vector最大的差異在于deque允許在常數時間内對起頭端進行元素的插入或移除操作,二在于deque沒有所謂容量(capacity)觀念,因為它是動态地分段連續空間組合而成,随時可以增加一段新的空間并連接配接起來。不會像vector那樣”因舊空間不足而重新配置一塊更大的空間,然後複制元素,再釋放舊空間“這樣的事情在deque是不會發生的。雖然deque也提供Random Access iterator,但它的疊代器并不是普通指針。除非特别需要盡可能選擇vector,而不是deque。

1.deque提供的接口

     deque提供的接口:包括得到deque的屬性接口、deque的操作接口以及構造函數:

   (1)構造函數:deque()、deque(size_type n、const T& value)、deque(size_type n)。

   (2)屬性函數:begin、end、size、empty、maxsize()、operator[]、front和back。

   (3)操作函數:push_back()、pop_back()、push_front、pop_front、erase()、resize()、clear()、insert。

2.deque的資料結構

      deque是一個分段連續的空間,維持了整體連續的假象,deque通過一個管控中心,來實作分段連續的結構:

STL源碼筆記之序列式容器

deque中iterator的設計:

template<class T,class Ref,class Ptr,size_t BufSize>
struct __deque_iterator{
    typedef __deque_iterator<T,T&,T*,BufSiz> iterator 
    typedef __deque_iterator self;
    typdef T** map_pointer;
    T *cur;
    T *firsr;
    T *lase;
    map_pointer node;//指向管控中心
    ...
}
           
STL源碼筆記之序列式容器

deque結構的設計(參考上圖), 主要考慮問題是buffer邊緣問題,超過上邊緣或者下邊緣的時候需要去map管控中心跳到下一個節點或者上一個節點。具體如下:

template<class T,class Alloc = alloc,size_t BufSize=0>
class deque{
public:
   typedef T value_type;
   typedef T* pointer;
public:
   typedef __deque_iterator<T,T&,T*,BufSiz> iterator;
protected:
   typedef pointer* map_pointer;
   iterator start;
   iterator finish;
   map_pointer map; 
   size_type map_size;
}
           

stack

    stack是一種先進後出(FILO)的資料結構。它隻有一個出口,形式如圖所示。stack允許新增元素、移除元素、取得最頂元素。但除了最頂元素外,沒有其他任何方法可以存取stack的其他元素。換言之,stack不允許有任何周遊行為。(stack沒有疊代器)

STL源碼筆記之序列式容器

         底層結構通過将deque為底部結構并封閉器頭端口,便可輕而易舉形成了一個stack。實際上stack可以用list作為結構,将一端封死就行了。

1.stack提供的接口 

     stack提供的接口:包括得到stack的屬性接口、stack的操作接口以及構造函數:

   (1)構造函數:stack()。

   (2)屬性函數:size、empty、top。

   (3)操作函數:push、pop。

注:pop傳回類型為void,是以經常以top 和pop一起使用。

2.stack的資料結構

template<class T,class sequece=deque<T> >
class stack{
protected:
   Sequence c;//所有的接口轉到調用C的接口但是隻操作一端
}
           

queue

    queue是一種先進後出(FIFO)的資料結構。它有兩個出口,形式如圖所示。stack允許新增元素、從底端移除元素、取得最頂元素。但除了底端可以加入,最頂端可以取出外,沒有其他任何方法可以存取queue的其他元素。換言之,queue不允許有任何周遊行為。(queue沒有疊代器)

STL源碼筆記之序列式容器

         底層結構通過将deque為底部結構,改一下接口使其符合”先進先出“的特性。實際上queue可以用list作為結構,将一端封死就行了。

1.queue提供的接口 

     stack提供的接口:包括得到stack的屬性接口、stack的操作接口以及構造函數:

   (1)構造函數:queue()。

   (2)屬性函數:size、empty、front和back。

   (3)操作函數:push、pop。

注:pop傳回類型為void,是以經常以front和pop一起使用。

2.queue的資料結構

template<class T,class sequece=deque<T>>
class queue{
protected:
   Sequence c;//這個跟上面不同的是兩端不封死
}
           

heap和priority_queue

     heap以vector為底層接口,并可通過sift_up和sift_down進行堆調整。heap提供的接口:make_heap、sort_heap、push_heap、pop_heap。下面舉一個例子:

#include<iostream>
#include<vector>
#include<algorithm> //必須包含這個頭檔案
using namespace std;


int main()
{	
   int a[5]={0,1,2,3,4};
   vector<int> ivec(a,a+5);
   make_heap(ivec.begin(),ivec.end());
   for(int i=0;i<ivec.size();++i)
	cout<<ivec[i]<<" ";
   system("pause");
   return 0;
}
           

     priority_queue有一個優先級的概念。它是利用一個make_heap完成,而heap又是以vector呈現。priority_queue提供的接口:

     構造函數:priority_queue(InputIterator first,InputIterator last,const Compare &x)

                       priority_queue(InputIterator first,InputIterator last)

     其他接口:size、empty、top、push和pop。

template<class T,class sequece=vector<T>,class Compare = less<typename sequence::value_type> >
class queue{
protected:
   Sequence c;
   Compare comp;
}
           

    下面舉一個例子,注意必須包括queue頭檔案。

#include<iostream>
#include<vector>
#include<queue>
//#include<algorithm>
using namespace std;


int main()
{
    int a[5]={0,1,2,3,4};
    priority_queue<int> ivec(a,a+5);
    while(!ivec.empty()){
        cout<<ivec.top()<<" ";
	ivec.pop();
    }
    system("pause");
    return 0;
}
           

slist

    slist是一種單連結清單結構,slist和list的差别:list提供的Bidirectional iterator疊代器,而slist提供的是Forward Iterator。slist和list共同具有的特色是他們的插入、移除、結合等操作并不會造成疊代器失效。插入操作會将新元素插入于指定位置之前,而非之後。這樣slist每次插入都要從頭周遊找到前一個節點。

1.slist提供的接口 

     slist提供的接口:包括得到slist的屬性接口、slist的操作接口以及構造函數:

   (1)構造函數:slist()。

   (2)屬性函數:begin、size、empty、front。

   (3)操作函數:front、pop_front、push_front。

2.slist的資料結構

struct __slist_node_base{
    __slist_node_base *next;//可以作為頭節點
}
template<class T>
struct __slist_node:public  __slist_node_base{
    T data;
}

struct __slist_iterator_base{
     typedef forward_iterator_tag iterator_category;
     __slist_node_base *node;
    ...
}
template<class T,class Ref,class Ptr>
struct  __slist_iterator: public __slist_iterator_base{
     typedef  __slist_iterator<T,T&,T*> iterator;
    ...
}

template<class T,class  Alloc=alloc>
class slist{
     typedef  __slist_node<T> list_node;
     typedef  __slist_iterator<T,T&,T*> iterator;
    ...
}
           

繼續閱讀