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())
else{cur += n;
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. 迭代器使用繼承,沒看懂好處在哪裡?