1. 程式人生 > >vector的clear()的內部過程與解構函式呼叫

vector的clear()的內部過程與解構函式呼叫

總結一下:

  1. vector中儲存了物件的指標,呼叫clear後,並不會呼叫這些指標所指物件解構函式,因此要在clear之前呼叫delete;
  2. 如果vector儲存的是物件,呼叫clear後,自建型別的物件(int之類的)直接刪除,若是外部型別,則呼叫解構函式。
class Test  
{  
public:  
    Test() { cout<<"Test cons"<<endl;}  
    ~Test() { cout << "Test des" << endl; }  
};  

int main()  
{  
    vector
<Test>
vec; Test* p1=new Test(); Test* p2=new Test(); Test* p3=new Test(); vec.push_back(*p1); vec.push_back(*p2); vec.push_back(*p3); //物件如何進行釋放,要呼叫已定義解構函式 vec.clear(); return 0; }

執行結果:
執行結果

對於這個結果也許你會覺得奇怪,為什麼push_back3次,卻呼叫了6次解構函式呢?這個就涉及到vector的構造與記憶體管理,《STL原始碼剖析裡面》講得很仔細。
vecor是這樣分配記憶體的:
const size_type len=old_size !=0?2*old_size :1;
也就是說,vector的容量永遠是大於或者等於size。而當記憶體不夠的時候,要重新分配空間,於是,allocate(len);再uninititalize_copy();之後再destroy(begin(),end());因此,這其中的記憶體變化是從1—>2—>4,所以當呼叫clear時,析構了6次。

下面是從網上摘錄的,實際為《STL原始碼剖析》內容:

// 清除全部元素。注意並未釋放空間,以備可能未來還會新加入元素。  
 void clear() { erase(begin(), end()); }  
//呼叫vector::erase的兩迭代器範圍版本  
 iterator erase(iterator first, iterator last) {  
      iterator i = copy(last, finish, first);  
      //finish在vector中定義表示目前使用空間的尾,相當於end(),clear呼叫時last=finish  
      destroy(i, finish); //全域性函式,結構的基本函式  
finish = finish - (last - first); return first; }

以上關鍵就是呼叫了destroy函式。destory函式在<stl_construct.h>中定義,為了便於分析整個的構造與釋放,將construct函式的內容也進行了摘錄。這其中要注意的是traits技術。

// destroy()單指標版本  
template <class T>  
inline void destroy(T* pointer) {  
    pointer->~T();   // 喚起 dtor ~T()  
}  



// destroy()兩迭代器版本  
//利用 __type_traits<> 求取最適當措施。  
template <class ForwardIterator>  
inline void destroy(ForwardIterator first, ForwardIterator last) {  
  __destroy(first, last, value_type(first));  
}  

//判斷元素的數值型別(value type)有 non-trivial destructor(自定義解構函式)  
template <class ForwardIterator, class T>  
inline void __destroy(ForwardIterator first, ForwardIterator last, T*) {  
  typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;  
  __destroy_aux(first, last, trivial_destructor());  
}  
// 如果元素的數值型別(value type)有 non-trivial destructor(自定義解構函式)  
template <class ForwardIterator>  
inline void  
__destroy_aux(ForwardIterator first, ForwardIterator last, __false_type) {  
 for ( ; first < last; ++first)    //遍歷元素進行析構  
    destroy(&*first);                  //!!!!!關鍵句!!!!!!!!!  
}  

//如果元素的數值型別(value type)有trivial destructor  
template <class ForwardIterator>   
inline void __destroy_aux(ForwardIterator, ForwardIterator, __true_type) {}  
//什麼都不做,STL是用了一種保守的方式,只有內建的元素型別(int,float等)進行判定trivial destructor的時候才是__true_type其他一切使用者自定義型別都是__false_type  


// destroy()兩迭代器版本,針對char*與wchar_t*的特化版本  
inline void destroy(char*, char*) {}  
inline void destroy(wchar_t*, wchar_t*) {}  

//僅僅是對placement new 的一層封裝  
template <class T1, class T2>  
inline void construct(T1* p, const T2& value) {  
  new (p) T1(value);    // placement new; 喚起 ctor T1(value);  
}  

看到這裡基本對上述的問題已經有答案了。
由於物件的指標不是內建物件,
for ( ; first < last; ++first) //遍歷元素進行析構
destroy(&*first);
*iterator是元素型別,&*iterator是元素地址,也就是一個指標。之後呼叫&*iterator->~T();所以可知當vector中所儲存的元素為物件的時候,呼叫clear()操作的時候系統會自動呼叫解構函式。但是當儲存元素是指標的時候,指標指向的物件就沒法析構了。因此需要釋放指標所指物件的話,需要在clear操作之前呼叫delete。

for(i= 0; i < vItem.size();i++)
      delete vItem[i];