條款2 儘量以const,enum,inline 替換#define
這個條款可以理解為“以編譯器替換前處理器”
由於前處理器(如#define等)並不被視為語言的一部分,所以讓你
#define ASPECT_RATIO 1.653
這樣定義時,標記名稱ASPECT_RATIO或許從未被編譯器看見,所以如果這樣執行時出錯,編譯錯誤資訊往往會提到1.653,如果標記名稱ASPECT_RATIO在一個並非你寫的標頭檔案裡,就會為了追蹤它在哪裡而浪費時間。
原因:你所使用的名稱可能並未進入記號表(symbol table)
解決方法:用一個常量替換上述的巨集#define:
const double AspectRatio=1.652;//大寫名稱通常用於巨集,這裡改變寫法
語言常量AspectRatio一定會被編譯器看到,因此會進入記號表
此外:對於浮點常量(就如上例),使用常量可能比使用#define導致較小量的碼。因為前處理器“盲目的將巨集名稱ASPECT_RATIO替換為1.653”可能導致目標碼內出現多份1.653
以常量替換#define時,兩種特殊情況
一:定義常量指標:
由於常量定義式通常被放在標頭檔案(方便被不同的原始碼含入),所以要把指標(並不是指標所指之物)宣告為const。
例如:如果要在標頭檔案定義一個常量(不變的)char*-based字串,必須寫const兩次
const char* const authorName="Scott Meyers";
但是通常string物件比其前輩char*-based更合適。所以往往可以這樣定義
const std::string authorName("Scott Meyers");
二:class專屬常量。
為了將常量的作用域(scope)限制於class內,必須讓它成為class的一個成員(member);而為了確保此常量最多有一份,必須讓它成為一個static成員:
class GamePlayer{ private: static const int NumTurns=5;//常量宣告式 int scores[NumTurns]; //使用該常量 ... };
這裡的NumTurns是宣告式,而非定義式。
通常C++要求你對使用的任何東西提供一個定義式。但如果它是個class專屬常量又是static且為整數型別(intergral type;例如ints,chars,bools),就需要特殊處理。只要不取它們的地址。就可以宣告並使用而無需提供定義式。
如果你要取其地址或者編譯器(不正確的)堅持要看到定義式,就必須提供一下定義:
const int GamePlayer::NumTurns;//記得吧式子放入標頭檔案而不是實現檔案
由於在宣告時獲得初值,因此定義時不可以再設初值
PS:無法用#define建立一個class專屬常量,因為#define並不重視作用域。一旦巨集被定義,它在其後的編譯過程中有效(除非被#undef).
所以#define不能定義class專屬常量,也不能提供封裝性。
舊編譯器也許不支援static成員在宣告式上獲得初值。此外“in-class初值設定”也只允許對整數常量進行。那就把初值放在定義式。
但如果class編譯期間需要 一個class常量值,如上述的Gameplayer::score即編譯器堅持要在編譯期間知道陣列大小。那麼可以改用”the enum hack“補償做法。
the enum hack補償做法:理論基礎:一個屬於列舉型別的數值可權充ints被使用。
例如:
class Gameplayer{
private:
enum{NumTurns=5};//"the enum hack"令NumTurns成為5的一個記號名稱
int scores[NumTurns];//ok
}
從這裡來看enum hack在行為方面類似於#define而不是const。
enum可以幫助你阻止別人獲得一個pointer或reference指向你的某個整數常量
define誤用,以它實現巨集,巨集看起來像函式,但不會招致函式呼叫帶來額外開銷。
錯誤示範:巨集夾帶巨集實參,呼叫函式f
#define CALL_WITH_MAX(a,b) f((a)>(b)?(a):(b))
int a=5,b=0;
CALL_WITH_MAX(++a,b);//a被累加2次
CALL_WITH_MAX(++a,b+10);//a被累加1次
出錯,呼叫f之前,a的遞增次數竟然取決於它被拿來和誰作比較
建議改為
template<typename T>
inline void callWithMax(const T& a,const T&b){
f(a>b?a:b);
}
callWithMAX是一個真正的函式,他遵守作用域和訪問規則
- 對於單純常量,最好用const物件或者enums替換#define
- 對於形似函式的巨集,最好改用inline函式替換#defines