之前看過一部分Effective C++,但是發現看了以後沒有真正的融入的實際的編程中,所以重新來學習一遍,並且做好筆記。在空閒的時候經常看看,把這些條款真正的應用在實際的編程中。
(1)條款01 視C++為一個語言聯邦
理解C++,必須認識其主要的次語言。共有四個:
C,Object-Orented C++, Template C++, STL
(2)條款02 盡量以const, enum, inline替換#define
如:#define VALUE 1,該記號名稱可能沒有進入記號表,所以當你在使用該變量產生一個編譯錯誤的時候,編譯器的錯誤信息中會提到 1 這個數值,但是沒有VALUE,這使得你無法准確的定位到錯誤。而是用const聲明常量就會解決這個問題。
class GamePlayer{
static const int num = 5;
int scores[num];
};
在舊式編譯器中也許不支持上述語法,他們不允許static成員在其聲明式上獲得初值。如果你的編譯器不支持在聲明式上賦值,可以再class的實現文件內進行定義。如:
class Test{
static const int num;
//在Test的頭文件中聲明
};
const int Test::num = 5;
//在Test實現文件內
但是在GamePlayer類中,你必須要在聲明數組scores時,給他一個確定的數值。這種情況下,可以使用“the enum hack“補償做法。其理論基礎是:“一個屬於枚舉類型的數值可權充ints來使用”,於是GamePlayer可定義如下:
class GamePlayer{
enum{num = 5};
int sorces[num];
};
對於enum hack方法來說,它的行為比較像#define,但是它能夠計入記號表,能夠避免編譯錯誤不好定位的問題,並且他不像const 常量,取一個const的值是可以的,但是不可以取一個enum的地址。所以當你不想讓別人獲得一個pointer或者reference指向你的某個整數常量,enum可以幫你實現這個約束。
使用#define實現類似於函數的宏,不會招致函數調用帶來的額外的開銷。但是使用這種宏經常會出現各種陷阱和麻煩。所以使用inline來代替類似於函數的這種宏。
(3)條款03:盡可能使用const
將某些東西聲明為const可幫助編譯器偵測出錯用法,const可被施加於任何作用域內的對象,函數參數,函數返回類型,成員函數本體
編譯器強制實施bitwiseconstness,但是你編寫程序時應該使用“概念上的常量性”
當const和non-const成員函數有著實質等價的實現時,令non-const版本調用const版本可避免代碼重復。
(4)條款04:確定對象使用前已先被初始化
讀取未初始化的對象會導致不明確的行為,在某些平台上,僅僅只是讀取未初始化的值,就可能使得程序終止。
所以在使用任何類型前都應該首先初始化,無論是內置類型還是自定義類型。對於無任何成員的內置類型,我們需要手工完成初始化,對於自定義類型,初始化的任務落在了構造函數中。另外,還需要了解賦值和初始化的區別。例如:
class Test{
int x;
stringy;
Test(int x, string y){}
};
Test:Test(){
this->x= x; //這是賦值操作,而不是初始化操作
this->y = y;
}
C++規定,對象的成員變量的初始化動作發生在進入構造函數本體之前。如Test的成員函數的初始化發生在default構造函數調用之前。Test構造函數的最佳寫法是,使用所謂的member initialization list替換賦值動作:
Test::Test(int x,string y):x(x),y(y)
//這實現了成員變量的初始化。
{
//do other thing
}
這樣使得成員變量在初始化的時候就設置了相對應的值,而不需要在經過一次賦值操作。提高了效率。
C++有著十分固定的“成員初始化次序”。基類總是更早於子類的初始化,而class成員變量的初始化總是以其聲明的次序初始化。
C++對於“定義在不同的編譯單元內的non-localstatic對象的初始化次序並無明確的定義”。解決這種問題的辦法是將每個non-local static對象搬到自己的專屬函數內。這些函數返回一個reference,用戶調用這些static函數,而不直接涉及到這些對象。這種手法的基礎在於:C++保證,函數內的local static對象會在“該函數被調用期間,首次遇到該對象”時被初始化。單例模式使用的就是這種手法。
摘自 cscmaker的專欄