《effective C++》讀書筆記(一)

1.類中遇到需要使用#define的地方要儘量避免。

1)       在類中避免

#define NUM 10

int array[NUM];

使用枚舉變量解決,即enumhack。

2)       在類中避免

#define MAX(a,b) f((a) >(b) ? (a) : (b))

使用templateinline解決

template <typename T>

inline void max(const T&a, const T& b)

{

  f(a > b ? a : b);

}

這樣不僅獲得了宏一樣的效率而且擁有具有和一般函數一樣的可預料行爲和類型安全性。而且max是個真正的函數它遵守作用域和訪問規則。

參考:《effective c++》p15,clause2

 

2.迭代器看成是指向類的指針。const迭代器是迭代器本身指向不能改變,const_iterator是該迭代器指向的內容不能改變。

 

3.const的使用。

1)       函數聲明式中使用const主要是爲了避免無意的錯誤。

2)       函數的返回值如果不需要改變應該定義爲const,如果需要改變應該定義爲引用或指針(可能破壞封裝性)。

3)       const成員函數可以訪問非const對象的非const數據成員、const數據成員,也可以訪問const對象內的所有數據成員;

4)       const對象只能調用const成員函數,不能調用非const成員函數。     

5)       const成員函數能被非const對象使用,也能被非const成員函數所調用。

6)       const成員函數只能調用const成員函數,即不能調用非const成員函數。(既然希望該函數不修改成員變量,那麼他所調用的函數自然應該也是const的)

7)       作爲一種良好的編程風格,在聲明一個成員函數時,若該成員函數並不對數據成員進行修改操作,應儘可能將該成員函數聲明爲const 成員函數。

8)       const成員函數可以訪問並修改類的非conststatic成員變量。(const對象是對象自身不能改變,而static是類公有的)

9)       。如果需要編寫類中成員的函數的const版本和非const版本,可以通過轉型(casting),可以運用函數的const版本實現出非const版本的方式簡化代碼。(詳見《effective c++》p24)

 

4.關於類的初始化

1)       創建對象的時候初始化。

2)       C++規定,對象的成員變量的初始化動作發生在進入構造函數本體之前。所以要在類的構造函數中使用初始化成員列表的方式初始化類成員,在構造函數內部對成員變量的操作都是賦值操作。

3)       規定總是在初始化列表中列出所有成員變量。以免還得記住哪些成員變量(以免他們在成員初始化列表中被遺漏的話)可以無需初值。

參考:《effectivec++》clause4

 

5.關於non-local static對象的初始化。

1)       所謂static對象,其壽命從被構造一直到程序結束爲止。

2)       函數內的static對象稱爲local static對象。

3)       global對象、定義於namespace作用域內的對象、在class內或在文件作用域內的對象稱爲non-local static對象。

4)       編譯單元:指產出單一目標文件的哪些源碼。基本上它是單一源碼文件加上其所包含的頭文件。

5)       如果某個編譯單元的non-local static對象的初始化動作使用了另一個編譯單元的non-local static對象,那麼將會產生未定義行爲。因爲無法保證使用到的non-local static對象已經初始化了。

6)       解決5)中的問題的方法是使用reference-return函數。即將每個non-local static對象搬到自己的專屬函數內(該對象在此函數內被聲明爲static),這些函數返回一個reference指向這個對象。即,將non-local static對象轉換爲local對象。

7)       這種方法還可以減少全局變量的個數,使對變量的使用可以監控。

例:ob源碼中使用了這種轉換方式。(雖然不是爲了解決初始化的未定義行爲問題,因爲沒有使用其他static變量)

 

注:任何一種non-const static對象,不論它是local或non-local,在多線程環境下“等待某件事情發生”都會有麻煩。解決方法是在單線程啓動階段手工調用所有的reference-return函數避免race condition。

參考:《effectivec++》clause4

 

6.如果你聲明瞭自己構造函數,那麼編譯器就不會再創建一個默認構造函數。

 

7.如果一個類作爲基類,並且定義了virtual函數,那麼必須定義virtual析構。但    virtual函數會增加開銷。

classes的設計目的如果不是作爲base class使用,或不是爲了具備多態性,就不應該聲明virtual析構函數。

參考:《effectivec++》clause7

 

8.在構造和析構期間不要調用virtual函數,因爲這類調用從不下降至derived class。

 

9.在定義operator=時如果允許出現x=y=z這種連鎖賦值的形式,就需要令operator=返回一個reference to *this。否則將operator=的返回值設置爲void,即禁止這種連鎖賦值操作。

 

10.在operator=中處理“自我賦值”

1)       對指針進行自我賦值可能存在自我賦值的安全性問題。(同一個對象delete兩次)

參考:《effectivec++》clause11

2)       賦值的效率問題。(自我賦值出現的頻率很少)

    
例:OB中

其中OB_LIKELY(this!=&other)就是證同測試。目的是爲了提高效率。

其中#defineOB_LIKELY(x)       __builtin_expect(!!(x),1)

__builtin_expect是GCC(version>=2.9)引進的宏,其作用就是幫助編譯器判斷條件跳轉的預期值,避免跳轉造成時間亂費。

參考:http://blog.csdn.net/sunnybeike/article/details/6802579

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章