vector的clear()的內部過程與解構函式呼叫
阿新 • • 發佈:2019-01-01
總結一下:
- vector中儲存了物件的指標,呼叫clear後,並不會呼叫這些指標所指物件解構函式,因此要在clear之前呼叫delete;
- 如果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];