1. 程式人生 > >C++ traits程式設計技法之__type_traits

C++ traits程式設計技法之__type_traits

//以下是鋪墊,__type_trivial的作用在後面,下面是一些概念性介紹。 

__type_trivial  雙底線字首,表示是SGI STL以外的東西,不在STL標準範圍之內。

__type_trivial負責萃取型別(Type)的特性,究竟是什麼特性呢?

         注:trivial是英文“無意義的、不重要”的意思。

         答曰:這個型別是否具備non-trivialdefalt ctor、non-trivial copy ctor、non-trivial assignment operator、non-trivial dtor? 這四個東西是什麼?

                     即:建構函式(ctor)、複製建構函式(copy)、 賦值函式(assignment)、解構函式(dtor)

 //以下針對原生指標設計__type_traits偏特化版本,原生指標被視為一種標量型別T

Template <class T>
Struct __type_traits<T*> {
         typedef  __true_type     has_trivial_default_constructor;
         typedef  __true_type     has_trivial_copy_constructor;
	 typedef   __true_type    has_trivial_assignment_operator;
         typedef  __true_type     has_trivial_destructor;
	 typedef   __true_type    is_POD_type;  //POD:plain old data
                                                //C++的內建型別或傳統的C結構體型別。POD型別必然有trivial ctor/dtor/copy/assignment四種函式</span>
}

關於__type_traits<T>怎麼判斷是否含有traits函式,《stl原始碼剖析》並沒有給出,這裡查閱資料,下面這個比較好理解:

如果至少滿足下面3條裡的一條:

1.      顯式(explict)定義了這四種函式。Ctor、copy、assignmen、dtor

2.      類裡有非靜態非POD的資料成員。比如string

3.      有基類

那麼上面的四種函式是non-trivial函式,比如叫non-trivial ctor、non-trivialcopy…,也就是說有意義的函式,裡面有一下必要的操作,比如類成員的初始化,釋放記憶體等。

例:

//整個T是POD型別</span>
class T
{
    //沒有顯式定義ctor/dtor/copy/assignemt所以都是trivial
    int a; //POD型別
};

//整個T1是非POD型別</span>
class T1
{
    T1() //顯式定義了建構函式,所以是non-trivial ctor
    {}
    //沒有顯式定義ctor/dtor/copy/assignemt所以都是trivial
    int a;//POD型別
    std::string b; //非POD型別
};

//基本概念介紹完畢,下面進入正題!


上面講了那麼多到底有什麼用處呢? 重點

如果這個類都是trivial ctor/dtor/copy/assignment函式,我們對這個類進行構造、析構、拷貝和賦值時可以採用最有效率的方法,不呼叫無所事事正真的那些ctor/dtor等,而直接採用記憶體操作如malloc()、memcpy()等提高效能,這也是SGI STL內部乾的事情。

如果你的類裡面只用到的基本型別,如int char double等,系統的預設解構函式其實什麼都沒有做
但如果你使用了其他的類如vectorstring等,系統的預設解構函式就會呼叫這些類物件的解構函式
如果是自己寫解構函式的話,如果你的類裡面分配了系統資源,如new了記憶體空間,打開了檔案等,那麼在你的解構函式中就必須釋放相應的記憶體空間和關閉相關的檔案;這樣系統就會自動呼叫你的解構函式釋放資源,避免記憶體洩漏

 

例如《STL原始碼剖析》2.2.3構造和析構基本工具:construct()和destroy(),關於destroy()部分原始碼如下:

//destroy()第一個版本,接受一個指標
Template <class T>
Inline void destroy(T * pointer) {
	Pointer-> ~T(); //呼叫dtor ~T();
}
//destroy()第二個版本,接受兩個迭代器。此函式設法找出元素的數值型別
//然後利用__type_trivial<>求取最適當措施。
Template <class ForwardInterator>
Inline void destroy(ForwardInterator first,ForwardInterator last, T*)
{
	Typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;
	__destroy_aux(first, last, trivial_destructor());
}

//下面是對__destroy_aux的過載,第三個引數分別為__true_type、__false_type
//如果元素的數值型別(value type)有non-trivial 函式
Template <class ForwardIterator>
Inline void
__destroy_aux(ForwardIterator first, ForwardIterator last, __false_type) {
	for( ; first < last ; ++first)
		destroy(&*first);  //對[first, last)範圍的多有物件析構掉!

}

//如果元素的數值型別(value type)有trivial 函式
Template <class ForwardIterator> 
Inline void __destroy_aux(ForwardIterator, ForwardIterator, __true_type) {}//什麼也不做


主要思想:destroy()有兩個版本,一個是接受一個指標,然後將該指標所指之物析構掉,直接呼叫該物件的解構函式即可。第二個版本接受first和last兩個迭代器,準備[first,last)範圍內的所有物件析構掉。我們不知道該範圍有多大,萬一很大,並且如果每個物件的解構函式都無關痛癢(既解構函式是trivial destructor),那麼一次次的呼叫這些沒有意義的解構函式對效率是一種傷害,例如上面所說的如果迭代器所指物件型別是int,那麼解構函式啥也沒有做,多次呼叫效率地下。所以!重點在這裡:我們先利用value_type()獲得迭代器所指物件的型別,再利用__type_traits<T>判斷該型別的解構函式是否無關痛癢(是否是trivial函式)。如果是(__true_type),則什麼也不做就結束;若否(__false_type),才會逐個對物件進行析構!

最後補充一點:究竟一個class什麼時候該有自己的non-trivial defalt ctor、non-trivial copyctor、non-trivial assignment operator、non-trivial dtor呢?

         答曰:一個簡單的判斷準則是:如果class內涵指標成員,並且對他進行記憶體動態配置,那麼這個class就需要實現出自己的non-trivial-xxx。換句話說,如果一個class內含有指標成員、並且對它進行了記憶體動態配置,那麼它必含non-trivial函式(前提是程式寫對--)。


參考資料:http://blog.csdn.net/a627088424/article/details/48595525

    《STL原始碼剖析》