1. 程式人生 > 其它 >條款2 儘量以const,enum,inline 替換#define

條款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