《STL源碼剖析》之vector


   STL中容器分為序列式容器和關聯式容器,其中vector作為最常用的序列式容器之一。

   vector基於array,准確的說是基於分配的連續內存,當內存不夠使用時,就在分配一塊內存,一般來說(源自《c++ primer》和《STL源碼剖析》)再分配內存是內存大小是前一大小的兩倍即可。這樣有效的防止連續空間在進行數據使用時超出范圍的問題。

  

 template<typename T,typename Alloc=alloc>
 class vector
 {
 public:
 typedef    T                    value_type;
 typedef   value_type*          pointer;
 typedef   ptrdiff_t             difference_type;
 typedef   value_type*           iterator;
 typedef   value_type&           reference;
 typedef   size_t                size_type;
 protected:
 typedef simple_alloc<value_type,Alloc> data_alllocator;
// .....                           其他的后面再說    
};

 

 

 

 

  

   我們可以看出來,這里vector的Iterator是T*,即vector<T>::iterator-->T*。當然這個顯而易見,這與后面的deque等的iterator差別很大。這樣在效率上就會有很好的保障,同樣安全性也會有很好的體現。

   在vector中有幾個函數用到的非常多:fill_initialize,uninitialized_fill_n,以及copy,第一個為對已分配內存進行初始化並且指示出開頭結尾,第二個為STL中的一個全局函數,用作將從heap得到的內存初始化。第三個就是數據轉移,這個函數對於vector屬於至關重要的一個。

  

//----------------------fill_initlialize------------------------------
  
  void fill_initialize(size_type n, const T& value)
{
start=allocate_and_fill(n,value);           //start已使用空間的頭部iterator
finish=start +n;                           //finish已使用空間的尾部iterator    
end_of_storage=finish;                    //end_of_storage分配空間的尾部iterator

}    

 

   

//------------------------alloccate_and_fill------------------------------
iterator allocate_and_fill(size_type n,const T&x)
{
iterator result=data_allocator::allocate(n);
uninitialized_fill_n(result,n,x);
return result;
}

   我們可以看出fill_initialize函數是將allocate_and_fill函數和iterator的開頭結尾工作進行合並。

 

  

//------------------------public函數部分體現--------------------------------
template<typename T,typename Alloc=alloc>
class vector
{//..............數據部分暫且不提及
public:
   iterator begin();
   iterator end();
   vector();
   vector(size_type n,const T&value);
   vector(int n,const T&value);
   vector(long n,const T&value);
  reference operator[](size_type n);
   size_type size();
   void push_back(const T&x);
//......其余省略
};

    從上面我們可以看出vector具有至少三個構造函數,這里對應了上面提及的fill_initialize函數,利用這個函數可以進行同一數據的多點多次存儲。

    vector必須有[]操作,因為我們在使用過程中往往可以將它看成一個無底數組,這樣大大增加了使用起來的便利程度。

    size函數這里的實現非常簡單,因為vector不同於deque。vector作為一種連續存儲容器,且Iterator作為typedef value* iterator可直接對iterator進行operator-或者operator+。

    push_back即首先判斷finish++>end_of_storage,如果是,則先分配較前者內存大小兩倍內存,然后進行大塊數據copy,在進行內存的initialize。

//--------------------insert,erase實現--------------------------------------
iterator erase(iterator first,iterator last)
{
iterator i= copy(last,finish,first);           //copy全局函數
destroy(i,finish);
finish=finish-(last-first);
return first;
}
iterator erase(iterator position)
{
if(position+1!=end())
copy(position+1,finish,position);
--finish;
destroy(finish);
return position;
}
//______________________________________
template<typename T,typename Alloc>
void 
vector<T,Alloc>::insert(iterator position,size_type n,
const T& x)
{
if(n!=0)
{
if(size_type(end_of_storage-finish)>=n)
{
T x_copy=x;
const size_type elems_after=finish-position;
iterator old_finish=finish;
if(elems_after>n)
{
uninitialized_copy(finish-n,finish,finish);
finish+=n;
copy_backward(position,old_finish,finish);
fill(position,position+n,x_copy);
}
else
{
uninitialized_fill_n(finish,n-elems_after,x_copy);
finish+=n-elems_after;
uninitialized_copy(position,old_finish,finish);
finish+=elems_after;
fill(position,old_finish,x_copy);
}
}
else
{   //備用空間小於新增元素個數
const size_type old_size=size();
const size_type len=old_size+max(old_size,n);
iterator new_start=data_allocator::allocate(len);
iterator new_finish=new_start;
//---------------分配空間及異常處理,不再續寫
}
}

      上圖作為insert操作的圖示可以看出,insert需要保存position點或者position段之后的數據,也就是說我們需要在插入之前把finish到position->first點之間的所有數據全部向后移動size(position)個位置。

       於是我們必須考慮以下情況:

    1 size(position)==0,那就不用移動了。

    2 為初始化空間尚能容下size(position),如果不能則需要重新分配更大空間然后進行數據復制,在進行insert操作

    3 size(position)>position->last-finish,先在finish后面初始化position的元素的size(position)-(last-finish)部分,把last到finish之間的所有全部后移,再把position前部放進去

    4size(position)<position->last-finish ,直接進行后移即可。

 

   

 

    erase操作不同於insert,他需要把數據磨滅即可,即不管從first部分到position從前向后移動或者是從finish到position從后向前移動都可以,但是問題就在於:

    1,vector不是deque,vector在頭部的操作復雜度太高,而deque則可以很好解決,故我們不需要判斷position的位置與(finish-first)/2的關系,deque仍須要判斷,因為在大容量數據的基礎上,這個仍然屬於考慮范圍,算法暫時無法進行優化或者進行switch,以得到特殊情況下的優化。

    2,不要忘了對finish到position移動后空余空間的數據placement delete,注意,此處不是operator delete,我們需要后面的分配的空間,但是不需要數據。   

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM