Tips01:View C++ as a federation of languages
把C++看作語言的聯邦,C++包括了C、 Object-Oriented C++(C with class)、 Template C++、 STL,
C++高效守則視情況而變化,取決于你使用C++的哪一部分,
Tips02:盡量以const, enum, inline替換#define
#define不被視作原始碼的一部分,在原始碼被編譯器處理之前就被前處理器處理了,所以當#define出現錯誤時,會很難去追蹤這個錯誤,最好的處理就是使用const常量代替宏,在替換時有兩點需要注意:
1、定義常量指標,
由于常量定義通常放在頭檔案內,所以指標也必須時const型別,因此在定義時必須const兩次,eg:
const char* const authorName = "xxxx";
2、定義類的專屬常量,
由于常量必須限制在類中,為了確保常量最多只有一份物體,所以它必須成為一個靜態成員,eg:
class GamePlayer{
private:
static const int NumTurns = 5;//宣告
int scores[NumTurns];
...
};
如果需要使用這個類的專屬常量的地址,需要在該檔案中提供該常量的定義,eg:
const int GamePlayer::NumTurns;//由于在宣告時已經獲得了初值,所以在定義的時候可以不用給予初值,
the enum hack補償做法:
理論基礎:一個屬于列舉型別的數值可權充int被使用,
class GamePlayer{
private:
enum(NumTurns = 5); //"the enum hack"令NumTurns成為5的一個記號名稱
int scores[NumTurns];
}
列舉可以阻止別人通過指標或者參考指向你的某個整數常量,即別人無法通過指標或者參考訪問列舉的常量,
使用inline函式代替宏定義的函式,
#define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b))
template<typename T>
inline void callWithMax(const T& a, const T& b)
{
f(a > b ? a : b);
}
Tips03:盡可能使用const
可以用const在class外部修飾global或者namespace作用域中的常量,或修飾檔案、函式、或區塊作用域中被宣告為static的物件,你也可以用它修飾class內部的static和non-static成員變數,而對于指標,你也可以指出指標自身、指標所指物,或者二者都是const,
const雖然變化多,但并不困難,如果const在星號左邊,則表示被指物為常量,如果在星號右邊,則表示指標自身是常量,如果出現在星號兩邊,則說明指標和被指物都是常量,
const成員函式
將const實施于成員函式的目的,是為了確認該成員函式可作用于const物件身上,
許多人漠視一件事實:兩個成員函式如果只是常量性不同,可以被多載
class TextBlock{
public:
...
const char& operator[](std::size_t position)const
{return test[position];}
char &operator[](std::size_t position)const
{return test[position];}
private:
std::string test;
}
int main()
{
TextBlock tb("HELLO WORLD!");
const TextBlock ctb("hello world!");
std::cout<<tb[0]<<std::endl;
std::cout<<ctb[0]<<std::endl;
return 0;
}
請記住
1、將某些東西宣告為const可幫助編譯器偵測出錯誤用法,const可被施加于任何作用域內的物件、函式引數、函式回傳型別、成員函式本體,
2、編譯器強制實施“bitwise constness”,但撰寫程式時應該使用概念上的常量
3、當const和non-const成員函式有著實質等價的實作時,令non-const版本呼叫const版本可避免代碼重復,
Tips04:確定物件在使用前被初始化
內置型物件
需要手動為內置型物件進行初始化,因為C++不保證初始化它們,
int t;
class Point{
int x, y;
};
Point p;
在某些情況下x 以及p的成員會被初始化,但有時候不會,這會導致初始值不明確,
通常如果你使用C part of C++(見條款1)而且初始化可能招致運行期成本,那么就不保證發生初始化,一旦進入non-C parts of C++,規則有些變化,這就很好地解釋了為什么 array(來自C part of C++)不保證其內容被初始化,而vector(來自 STL part of C++)卻有此保證,
最好的處理辦法就是:永遠在使用物件之前將他初始化,對于無任何成員的內置型別,必須手工完成,
非內置型物件
對于內置型別意外的任何東西,初始化責任就放在了建構式身上,規則很簡單:確保每一個建構式都將物件的每一個成員初始化,
建構式的一個較佳的寫法是,使用member initialization list(成員初始化串列)替換復制動作,
class PhoneNumber{...};
class ABEntry{
public:
ABEntry(std::string name, std::string address)
:theName(name), theAddress(address), numTimesConsulted(0){};
private:
std::string theName;
std::string theAddress;
int numTimesConsulted;
};
如果在定義時,沒有給引數,我們可以再額外重構一個無引數的建構式,
class PhoneNumber{...};
class ABEntry{
public:
ABEntry(std::string name, std::string address)
:theName(name), theAddress(address), numTimesConsulted(0){};
ABEntry():theName(), theAddress(), numTimesConsulted(0){};
private:
std::string theName;
std::string theAddress;
int numTimesConsulted;
};
有些情況下即使面對的成員變數屬于內置型別,也一定要使用初始化串列,比如成員變數為const 或 references,他們必須有初值,而且不能被賦值,為避免需要記住成員變數何時需要在成員初始化列中初始化,何時不需要,最簡單的做法就是:使用成員初始化串列,這樣做有時候絕對必要,而且比賦值更加高效,
C++ 有著固定的成員初始化次序,次序總是相同:base ckasses 更早于其derived ckass被初始化,而class的成員變數總是以宣告次序被初始化,
不同編譯單元內定義之non-local static物件
static物件指其壽命從被構造出來直到程式結束位置,這種物件包括global物件、定義于namespace作用域內的物件、在class內、函式內、以及file作用域內被宣告的static物件,函式內的static稱為local static物件,其他static物件稱為non-local static物件,程式結束時static物件會自動銷毀,
編譯單元指產出單一目標檔案的那些原始碼,基本上它是單一原始碼加上其所含入的頭檔案,
所以本部分主要涉及至少兩個原始碼檔案,每個內都至少含有一個non-local static物件,問題在于如果某編譯單元內的某個non-local static物件的初始化作用使用了另一個編譯單元內的某個non-local static物件,它所用到的這個物件可能尚未被初始化,因為C++對“定義于不同編譯單元內的non-local static物件”的初始化次序并無明確定義,
解決辦法:將每個non-local static物件搬到自己的專屬函式內(該物件在此函式內被宣告為static),這些函式回傳一個reference指向它所含的物件,然后用戶呼叫這些函式,而不直接指涉這些物件,換句話說:non-local static物件被local static物件替換了,(單例模式)
總結
1、為內置型物件進行手工初始化,因為C++不保證初始化它們,
2、建構式最好使用成員初始化串列,而不要在建構式內使用賦值操作,初始列列出的成員變數,其排列次序應該和它們在class中的宣告次序相同,
3、為避免“跨編譯單元之初始化次序”問題,請以local static物件替換non-local static物件,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/243634.html
標籤:區塊鏈
