第二章開始作者定義了乙個簡單的模板類jj::allocator
,有allocator的所有介面,包括allocator()
deallocate()
construct()
destroy()
四個成員函式 。但是開啟 stl原始碼中的stl_alloc.h
,sgi stl用的最多的std::alloc
並沒有construct()
destroy()
成員函式,sgi stl在這一點上沒有遵守stl規範。construct() 和 destroy()被宣告為全域性函式。
construct() 和 destroy() 全域性函式定義於stl_construct.h
。construct()呼叫new操作符,沒什麼好說的,這裡主要說說destroy()。
destroy()可以接受乙個指標作為引數,呼叫相應的析構函式,如下所示。
template
inline
void destroy(_tp* __pointer)
template
inline
void _destroy(_tp* __pointer)
這是泛化的函式模板,如果是基本型別的指標,顯然無法呼叫析構函式,書上說有基本型別指標的特化函式模板,但實際上原始碼裡是非模板函式,由於非模板函式優先順序高於模板函式,遇到類似int*的指標編譯器會呼叫非模板函式,並且不做任何操作,如下所示。
inline
void _destroy(char*, char*) {}
inline
void _destroy(int*, int*) {}
inline
void _destroy(long*, long*) {}
inline
void _destroy(float*, float*) {}
inline
void _destroy(double*, double*) {}
destroy()還可以接受兩個iterator引數,來析構乙個區間的所有物件。這就有乙個問題,如果這個區間很大,而每個物件的析構函式都是無關緊要的(即trivial destructor,析構函式裡什麼都不需要做),那麼乙個個呼叫析構函式很影響效率。所以需要判斷,此物件的析構函式是否是non-trivial。
template _forwarditerator>
inline void destroy(_forwarditerator __first, _forwarditerator __last)
template _forwarditerator>
inline void _destroy(_forwarditerator __first, _forwarditerator __last)
/*****************萃取__type_traits::has_trivial_destructor***************/
template _forwarditerator, class
_tp>
inline void
__destroy(_forwarditerator __first, _forwarditerator __last, _tp*)
/*******************如果no-trivial destructor 依次destroy***********************/
template _forwarditerator>
void
__destroy_aux(_forwarditerator __first, _forwarditerator __last, __false_type)
/*****************如果trivial destructor 不做任何操作*************************/
template _forwarditerator>
inline void __destroy_aux(_forwarditerator, _forwarditerator, __true_type) {}
因為要在編譯期就決定是乙個個呼叫析構函式還是什麼都不做,所以不可能用if…else…語法(執行期才去執行),而是依靠template引數推導。如上**,有兩個函式模板__destroy_aux()
,最後乙個引數是_false_type
還是__true__type
決定了destroy()的操作內容,而這個引數又是從type_traits中萃取出來的,在第3點我們就來講講這個type_traits。
這實際上就是泛型程式設計的常用技巧了,編譯期完成的工作不要到執行期再去做,而是依靠template引數推導等技巧來完成類似於if…else…的判斷。
這一部分放在了第三章iterator,在講完了可以萃取特性的iterator_traits後順帶引出可以萃取更多特性的type_traits,更容易讓人理解。不過要在第二章allocator碰到type_traits就要黑人問號了。所以在這裡說明一下。
type_traits就是告訴編譯器以下資訊,這個型別是否一定要執行預設建構函式(has_trivial_default_constructor),是否一定要執行拷貝建構函式( has_trivial_copy_constructor),是否一定要執行賦值運算子(has_trivial_assignment_operator),是否一定要執行析構函式(has_trivial_destructor),是不是pod型別(is_pod_type)。舉乙個char特化模板的例子如下。
struct __true_type ;
struct __false_type ;
__stl_template_null struct __type_traits;
本來我也不太理解為什麼要用兩個struct來表示__false_type
和__true_type
,直到又去回味destroy()原始碼才明白,仔細看以下兩行。
typedef typename __type_traits<_tp>::has_trivial_destructor
_trivial_destructor;
__destroy_aux(__first, __last, _trivial_destructor());
因為是要用template引數推導來確定特化哪乙個__destroy_aux(),所以就需要兩個class object來讓編譯器進行引數推導,於是定義了兩個空的struct。 STL原始碼剖析讀書筆記
一.stl提供六大元件,彼此可以組合套用。1.容器 containers 各種儲存結構,如vector list deque set map,用來存放資料。2.演算法 algorithme 各種常用演算法,如sort search copy erase等。3.迭代器 iterators 扮演容器與演...
STL原始碼剖析讀書筆記之vector
stl原始碼剖析讀書筆記之vector 1.vector概述 vector是一種序列式容器,我的理解是vector就像陣列。但是陣列有乙個很大的問題就是當我們分配 乙個一定大小的陣列的時候,起初 也許我們不會覺得陣列容量太小不合需求,但是隨著資料量的增加,陣列尺寸 大小不再滿足需求,此時我們需要手動...
《STL原始碼剖析》讀書筆記 三
hashtable 非標準 二叉搜尋樹 任何節點最多只能允許兩個子節點 平衡二叉樹 確保整棵樹的深度為o logn 左右子樹的高度最多差1 setmap multiset 特性與用法與set完全相同,唯一的差別在於它允許鍵值重複,插入的時候用的是rb tree的insert equal 而不是ins...