1. 程式人生 > >為什麼 C++ 中提倡儘量避免使用巨集 #define(轉)

為什麼 C++ 中提倡儘量避免使用巨集 #define(轉)

C++ 的書上常說,儘量不要用 #define 來定義常量。這究竟是為什麼呢?

其實 C++ 並不僅僅不提倡用巨集來定義常量,而且還不提倡用巨集來定義“函式”。事實上 C++ 並不是很喜歡預處理巨集,在很多很多方面,如果不是必需,儘量不要使用預處理巨集。

為什麼 C++ 不喜歡預處理巨集?

首先,預處理巨集是“全域性”的。所以,在 C++ 這樣如此強調名稱空間、類這樣的東西的語言中,全域性的東西真是越少越好。但是其實預處理巨集的全域性並不是語義上的全域性,之所以叫預處理巨集,是因為預處理巨集會在編譯器編譯程式碼之前被簡單地替換成程式碼

然後,正因為預處理巨集會被簡單替換,所以替換的結果是不可預料的。這些說起來還是比較模糊,所以下面將舉一些實際的例子。

#define 會把其後的註釋也簡單地替換


#define PI 3.14159 // This is a constant.

/* Some harmonious code */

double radius = 2.0;
double area = PI * radius * radius
;

cout << area << endl;

某些編譯器的結果會輸出 3.14159 . 但是使用了 g++ 的測試結果卻是正確的。在輸出錯誤結果的編譯器下,是因為上面使用了 PI 的那行被替換成了:

double area = 3.14159 // This is a constant. * radius * radius


其實這種情況可以用下面的方法解決:

#define PI 3.14159 /* This is a constant */

儘管可以如此解決,我們還是不提倡使用 #define 定義常量。我們還是有一萬個理由,儘量不用 #define .

巨集並不能正確地指定型別

我們知道 #define 定義的常量並不需要指定型別,而用 const 修飾的常量是必須指定型別的。這個方面其實沒有太多的例子可以舉,而且我也沒有想出一些明顯的錯誤的例子,所以就提一下吧。

C 裡必須用巨集定義常數而 C++ 並不一定

考慮以下這段程式碼:

const int n = 256;
char a[n] = {0};

這段程式碼如果以 .c 作為字尾儲存,會提示定義陣列時需要一個常量作下標,因為在 C 語言中,const 只是“不可修改的變數”之意。所以在 C 裡只能用 #define 定義常量。但是在 C++ 中卻可以用 const.

這些程式碼在 VC6 和 gcc 中測試過,都是如上所述。

用 #define 定義字面常量可能會浪費很多空間

比如在程式碼中使用 #define 定義了一個比較長的常量字串,如果這個巨集被使用了很多次,那麼這個字面常量將會遍地開花,如果編譯器沒有那麼聰明的話,可能會耗費很多不需要耗費的空間。

用 #define 定義常量物件可能會執行多次建構函式而降低時間效率

參見如下程式碼:

#define WELCOME_MESSAGE string("Welcome!")

如果多次使用 WELCOME_MESSAGE 巨集的話,將有可能每次遇到它們的時候都呼叫 class string 的 string(const char *) 建構函式。這樣的話,不僅空間會被浪費,而且也會影響執行效率。

#define 定義的“函式”“引數”也只是簡單替換

考慮如下程式碼:

#define max(a,b) (a > b ? a : b)

/* Some harmonious code */

int x = 5, y = 6;
int n = max(++x, ++y);

這樣之後,n 不會是 7. 因為那個呼叫 max 巨集的那行被替換成了:

int n = (++x > ++y ? ++x : ++y);

所以 n 的值會是 8. 其實這種小錯誤還算不錯了,還有更錯的。

#define 定義的函式不“認識” C++ 裡的 template

當 #define 出生的時候,還沒有 template, 似乎也沒有 // 開頭的註釋。考慮如下程式碼:

#define max(a,b) (a > b ? a : b)

template
class example {
/* ... */
public:
bool operator>(const example &foo) const {
/* ... */
}
};

/* ... */

template x, y;
/* ... */
template n = max(example(x), example(y));

這裡的最後一行的 max 巨集會把 example<float 視為 a, double>(x) 視為 b. 後面的東西就會報錯了。在 C 語言裡只考慮到了括號內的逗號,而沒考慮大小於號裡的括號。

但是為什麼 C++ 又沒有放棄支援 #define

C 語言裡有一個 assert.h , 是必須要用巨集來實現的。除此之外,C++ 似乎有一種在執行時獲取“類”的名字的思路,就是使用巨集。還有,巨集可以避免標頭檔案被多次包含。而且有的時候需要一些預定義的巨集,來控制程式碼在不同的環境下的編譯。

有的時候,對於一些要重複多次,並且比較長的程式碼,可以在區域性啟用巨集。但是在用完的地方一定要使用 #undef 將其取消。但是大多時候,儘量不要用巨集來定義常數和“函式”。

那在 C++ 中應該怎麼辦才能替代 #define 的一些功能

C++ 不但能替代一些 #define 沒有完善的功能,而且能做得更好。對於常數定義,在 C++ 中使用 const 就可以了。對於一些簡單的“函式”,可以使用 C++ 的 inline 修飾符,使用這個修飾符,效果上會和一般的函式一樣,但是實際上編譯器會把函式中的程式碼根據語義替換到呼叫函式的地方,所以執行效率不會受到太大影響。而且在使用 max(++i, ++j) 之類的函式的時候,不會是簡單替換,所以 ++i 和 ++j 分別只被計算一次。