1. 程式人生 > >STL之序列式容器難點分析

STL之序列式容器難點分析

Vector容器

又稱作變長陣列,隨著元素的增加,其內部機制會自行擴充空間以容納新元素。

其實現的原理是:在vector中有三個容器分別表示容器目前使用空間的頭,尾和可用空間的尾。即容器分配的記憶體要大於等於實際使用的記憶體。

如果vector無使用記憶體的話,容器會重新分配一塊更大的空間,然後將資料移動到新的空間,然後將舊空間釋放掉。

所以vector有兩個成員函式,size()代表使用空間的大小;capacity()代表可用空間的大小


由於vector採用的連續空間,所以vector的迭代器是原型指標。

template<Class T, class Alloc= alloc>

class vector{

public:

typedef T value_type;

typedef value_type*  iterator;

}

vector最關鍵的技術是insert函式

template<class T, class Alloc>

void vector<T, Alloc>::insert_aux(iterator position, const T& x){

if(finish != end_of_storage){  //還有備用空間

construct(finish , *(finish - 1));

//調整水位

++finish;

T x_copy = x;

copy_backward(position, finish - 2, finish - 1);// 將[first, last )區間內的每一個元素,即[position, finish-2),以逆行的方向複製到以result-1即(finish -1)起點的區間

*position = x_copy;

}

else

{

const size_type old_size = size();

const size_type len = old_size != 0 ? 2 * old_size : 1; //如果空間分配不足,按照2倍空間分配

iterator new_start = data_allocator::allocate(len);

iterator new_finish = new_start;

try{

new_finish = uinitalized_copy(start , position,new_start);

construct(new_finish,x);

++new_finish;

new_finish = uinitalized_copy(position + 1, finish, new_finish);'

}

catch{

destroy(new_start, new finish);

data_alloctor::deallocate(new_start, len);

}

}

destory(begin(),end());

deallocate();


start = new_start;

finish = new_finish;

end_of_storage = new_start + len;

}

//由於vector 成員函式的操作中使用到了copy 函式,需要對copy函式進行解析

copy函式---強化效率無所輸入不用其極,模板偏特化,函式過載,型別特性等

copy函式:將輸入區間[first ,last) 內元素複製到輸出區間[result, result + (last - first)) copy演算法對template引數所要求的條件非常寬鬆,

其輸入區間只需由InputIterator,輸出區間只需由OutputIterator,拷貝演算法特別通用。

copy 完全泛化版本:

template<class InputIterator ,  class OutputIterator>

inline OutputIterator copy(InputIterator first, InputIterator last, OutputIterator result)

{

return _copy_dispatch<InputIterator , OutputIterator > ()(first, last , result);  // 仿函式臨時物件

}

//特化版本,採用的過載技術

inline char* copy(char * first ,  char * last , char * result )

{

memmove(result,  first , last - first);

return result + (last - first);

}

inline wchar* copy(wchar * first ,  wchar * last , wchar * result )

{

memmove(result,  first , last - first);

return result + (last - first);

}


//泛化版本的仿函式

template<class InputIterator , class OutputIterator >

struct _copy_dispatch

{

OutputIerator operator()(InputIterator  first, InputIterator last, OutputIterator result){

return _copy(first ,last ,result , Iterator_category_category(first));

}

};

//特化版本的仿函式

template<T> 

struct _copy_dispatch<T* ,T*>

{

T* operator()(T* first , T* last, T* result)

{

typedef typename _type_traits<T>::has_trivial_assignment_operator t;

return _copy_t(first , last , result , t());  //特化版本一種是通過memmove 、 _copy_d

}

};

template<T> 

struct _copy_dispatch<const T* ,T*>

{

T* operator()(T* first , T* last, T* result)

{

typedef typename _type_traits<T>::has_trivial_assignment_operator t;

return _copy_t(first , last , result , t());

}

};


//過載函式根據迭代器的型別

template<class InputIterator , class OutputIterator>

inline OutputIterator _copy(InputIterator first, InputIterator last, OutputIterator result, input_iterator_tag)

{

for(; first ! = last ; last++)

{

*result = *first;

}

return result;

}


template<class RandomAccessIterator , class OutputIterator>

inline OutputIterator _copy(RandomAccessIterator first, RandomAccessIterator last, OutputIterator result, random_access_iterator_tag)

{

return _copy_d(first , last , result , distance_type(first) );

}


temlate<class RandomAccessIterator , class OutputIterator, class Distance>

inline OutputIterator _copy_d(RandomAccessIterator first, RandomAccessIterator last , OutputIterator result, Distance*)

{

for(Distance n = last - frist ; n > 0;--n ,++result, ++first){

*result = * first;

}

return result;

}


list容器:

list容器是一個普通資料結構中的雙向連結串列。

其節點的結構如下:

template<class T>

struct _list_node{

typedef void*  void_pointer;

void_pointer * pre;

void_pointer * next;

T  data;

}

list實現採用的是環形的雙向連結串列,所以list的迭代器型別為Bidirectional Iterators

_list_iterator的設計其中必須存在一個執行list節點的指標即

template<class T, class Ref, class Ptr>

struct _list_iterator

{

typedef  _list_iterator<T,Ref,Ptr> self; //為了實現迭代器之間的比較

typedef  _list_node<T> * link_type;

link_type node;//用於接收指標,遞增,遞減,比較等操作

}

list操作可以參考下,作為設計連結串列的參考程式碼:

連結串列的插入:

template<class T>

iterator insert(iterator position , const T& x)

{

link_type tmp = create_node(x);

tmp->next = position.node;  //新節點的next 指向postion節點

tmp->pre = position.node->pre;//新節點的pre執行position節點的pre

(link_type(position.node)->pre)->next =  tmp;// position節點的pre的next指向tmp

position.node->pre = tmp;// position的前驅pre指向tmp

return tmp;

}

連結串列的刪除:

iterator erase(iterator position){

link_type pre_node = link_type(position.node->pre);

link_type next_node = link_type(position.node->next);

pre_node->next = next_node;

next_node->pre = pre_node;

destroy(positon.node);

return iterator(next_node);

}


///deque 是一種雙向開口的連續線性空間。

deque的特點:允許於常數時間內對起頭端進行插入或移動操作;deque沒有所謂的容量觀念。

deque實現原理:

deque採用一塊所謂的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;


typedef _deque_iterator<T, T&,T*,BufSiz> iterator;

protected:

typedef pointer* map_pointer;//指標的指標

iterator  start;

iterator finish;

map_pointer  map;   //中控器

size_type  map_size; //管理記憶體指標的大小

}


template<class T, class Ref, class Ptr, size_t BufSiz>

struct _deque_iterator{

typedef _deque_iterator<T, T&, T*,BufSize> iterator;

typedef _deque_iterator<T, const T&,const T*,BufSiz> const_iterator;

static size_t buffer_size(){return _deque_buf_size(BufSiz, sizeof(T));}


typedef random_access_iterator_tag iterator_category;

typedef T value_type;

typedef Ptr pointer;

typedef Ref reference;

typedef size_t size_type;

typedef ptrdiff_t difference_type;

typedef T** map_pointer;


typedef _deque_iterator self;

T*  cur;

T* first;

T* last;

map_pointer node;

}

deque實現的關鍵是通過迭代器的operator操作符,接下來一一分析

void set_node(map_pointer  new_node)

{

node = new_node;

first  =  * new_node;

last = first + difference_type(buff_size());

}


reference operator* () const { return *cur};

pointer operator->()  const {  return &(operator*() ) ; }


difference_type operator- (const self &) 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); //移動到下一個節點

cur = first; //令當前節點等於第一個節點,迭代器設計

}

return * this;

}

self operator++(int)//後置式標準寫法

{

self tmp = *this; //返回本迭代器節點

++*this;               //採用前置法

return tmp;

}

self& operator--()

{

if(cur == first)

{

set_node(node -1);

cur =last;

}

--cur;

return *this;

}

self operator--(int)

{

self tmp = *this;

--*this;

return tmp;

}

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)

{

self tmp = *this;

return tmp += n;

}

self operator-=(difference_type n)

{

return *this +=  -n;

}

self operator-(difference_type n)

{

self tmp = *this;

return tmp -=  n;

}

reference operator[](difference_type n) const  {return *(*this + n); }

//迭代器的上述操作,能夠滿足deque的插入,訪問等。如果空間不足的時候 需要調整中控器的大小。


//stack棧

stack是一種配接器,它是以改變底層容器的介面為實現,稱為配接器。

由於棧只能先進後出的資料結構,可以通過deque和list作為底層實現的容器,其實用vector也可以實現。


//queue佇列

佇列是一種先進先出的結構

queue也是一種配接器,也是通過底層容器實現,可以由deque和list為底層實現的容器,其實用vector也可以實現


//heap堆

完全二叉樹,可以利用array來儲存所有節點。將array的0號元素保留,那麼某個節點位於array的i處,左子節點 必位於2i處,右子節點位於2i+1處。

我們採用vector作為儲存介質,然後配合heap的插入,刪除,取極值等演算法組成。

//插入演算法,保持堆的特性

template<class RandomAccessIterator>

inline void push_heap(RandomAccessIterator first , RandomAccessIterator last)

{

_push_heap_aux(first,  last , distance_type(first),  value_type(first) );

}

template<class RandomAccessIterator, class Distance, class T>

inline void _push_heap_aux(RandomAccessIterator first, RandomAccessIterator last, Distance* , T*)

{

_push_heap(first, Distance((last - first) - 1), Distance(0) ,  T(*(last - 1)) );

}

template<class RandomAccessIterator , class Distance, class T>

inline void _push_heap(RandomAccessIterator first, Distance holeIndex ,  Distance topIndex,  T value)

{

Distance parent = (holeIndex - 1) / 2;   //找到父節點

while(holeIndex > topIndex &&  *(first + parent) <  value) //判斷當前節點是否為頭結點,以及 當前節點的值是否大於父節點的值

{

*(first + holeIndex) =  *(first +parent);    //移動父節點的值

holeIndex = parent;                                  //更換holeIndex的位置

parent = (holeIdex  - 1 )/ 2;                     //重新計算父節點的位置

  }

*(first + holeIndex) =  value;                                //將新值插入

}


//刪除演算法,保持堆的特性

template<class RandomAccessIterator , class Distance, class T>

void _adjust_heap(RandomAcessIterator first, Distance holeIndex, Distance len , T value)

{

Distance topIndex = holeIndex;   //保留topIndex

Distance secondChild = 2 * holeIndex + 2; //取右子節點

while(secondChild < len)   //小於最大長度

{

if(*(first + secondChild)  <  *(first + secondChild  -1 ))// 比較子節點大小

sencondChild--;

*(first + holeIdex) = *(first + secondChild);  //將子節點移動到移除節點位置

holeIndex = secondChild;

secondChild = secondChild * 2 + 2;

if(secondChild == len)

{

*(first + holeIndex) = *(first  + secondChild  -1 );

holeIndex = secondChild - 1;

}

_push_heap(first, holeIndex , topIndex, value);

}


//優先順序佇列priority_queue

優先順序佇列是以heap為底層容器的配接器。


//slist單向連結串列

單向連結串列與雙向連結串列相比的特點:

1. 迭代器是InputIterator 單向迭代器

2. 類只是先了push_front 和popfront,因為使用insert插入只能插在其前面不能插在後面

3. 迭代器使用繼承,沒看懂好處在哪裡?