1. 程式人生 > >巨集在C++中的替代解決方案

巨集在C++中的替代解決方案

 巨集,在C語言中是個神的存在,能夠玩出各種花樣,也正因為此,才會給普通程式設計師造成不少的困擾。由於巨集只在預編譯階段起作用,使得編譯器無法檢測其中的Bug,作為新時代的C++程式設計師,還是遠離的好。

C++為巨集提供了一些替代的解決方案,嗯,是一些。

  1. 常量定義
#define NUM 100

《EffectiveC++》的第一個條款,討論的就是這個巨集。由於巨集是預編譯程式來處理,所以NUM這個名字不會加入到符號表中,如果出現編譯錯誤時,提示資訊中就不會出現NUM,而是100,為排除錯誤增加了額外的障礙。

替代方案就是使用const來定義常量,或者使用列舉enum。

const int NUM = 100;

const常量放在標頭檔案中,也不必擔心存在多個例項的問題,對於const修飾的變數,編譯器一般也會對其進行優化,不會出現多重定義的問題。

C語言中還有一個特殊的常量定義:NULL。其一般的定義為 #define NULL 0,指標的內容卻是一個整型,這不符合常理。所以在C++11中使用nullptr代替了NULL。

2.函式定義

由於巨集只是在程式碼中做字串替代展開,所以,用巨集定義的函式,實際上並沒有減少程式碼的體積。另外,還有一些天然的缺陷,假設一個求平方的函式巨集

[cpp] view plain copy
#definesquare(x)  (x*x)  
voidf(double d, int i)
{ square(d); //OK square(i++); //糟糕, (i++*i++) square(d+1); //更糟,(d+1*d+1) }

縱然可以把引數加上括號來解決,#define square(x) ((x)*(x)),但i++被執行兩次這個問題還是無法解決。

C++中的替代方案,就是使用inline函式。

[cpp] view plain copy
inline int square(intvalue)
{
return value*value;
}

inline函式具有函式的性質,引數傳遞不管是傳值還是傳引用,都不會對引數進行重複計算;同時會對引數做型別檢查,保證程式碼的正確性;inline函式也是在程式碼中做程式碼展開,效率上並不比巨集遜色。

如果整型不滿足需求,還可以定義為模板:

[cpp] view plain copy
template
inline T square(T& value)
{
return value*value;
}

還有一種更離譜的函式定義形式:

[cpp] view plain copy
#defineNull_Check(p)\  
if(p == NULL) return;  

這種使用反斜槓定義的函式,更得注意,如果在反斜槓後多了個空格的話,有的編譯器會出現變異錯誤,而提示資訊嘛,你可能會困擾很久的。因為反斜槓會對空格這個字元做反義,就會在程式碼中的引入非法字元,人眼是很難發現這種錯誤的。

3.型別重定義

#defineDWORD unsigned int

這種型別重定義完全可以使用 typedef unsigned int DWORD 來替代。

4.條件編譯

[cpp] view plain copy
#ifdefSystemA  
testA();  
#else//SystemB  
testB();  
#endif  

這種條件編譯巨集,一般在不同的產品或平臺使用同一套程式碼的情況,大量出現。定義了SystemA的時候,SystemB的程式碼是不編譯的,也就意味著你的程式碼沒有時刻處於編譯器的監控中。可以使用template技術來解決。

[cpp] view plain copy
constint SystemA = 1;
constint SystemB = 2;

template
void test()
{}
//定義不同的系統的特化版本
template<> void test(){ //SystemA的實現 }
template<> void test(){ //SystemB的實現 }

這樣,不同的系統使用自己的模板即可,別人的程式碼也會同時接受編譯器的檢查,不至於出現遺漏編譯錯誤的情況。

5.標頭檔案包含

[cpp] view plain copy
#ifndeftest_h  
#definetest_h  
    //test.h的實現  
#endif  

為了防止標頭檔案重複包含,C++中現在也只能這麼做,目前沒有別的替代方案。且看看原委,Bjarne Stroustrup在《C++語言的設計與演化》一書中,提供了一個include的設計,可惜的是並沒有真正實現。(Cpp, 即C語言前處理器)

我曾經建議可以給C++本身增加一個include指示字,作為Cpp的#include的替代品。C++的這種include可以在下面三個方面與Cpp的#include不同:

1)如果一個檔案被include兩次,第二個include將被忽略。這解決了一個實際問題,而目前這個問題是通過#define和#ifdef,以非常低效而笨拙的方式處理的。

2)在include的正文之外定義的巨集將不在include的正文內部展開。這就提供了一種能夠隔離資訊的機制,可以使這些資訊不受巨集的干擾。

3)在include的正文內容定義的巨集在include正文之後的正文處理中不展開。這保證了include正文內部的巨集不會包含它的編譯單位強加上某種順序依賴性,並一般地防止了由巨集引起的奇怪情況。

對於採用預編譯標頭檔案的系統而言一般地說,對於那些要用獨立部分組合軟體的人們而言,這種機制都將是一個福音。請注意,無論如何這還只是一個思想而不是一個語言特徵。

也就是說,這個想法在C++中並沒有實現。

如果你沒有很好的駕馭巨集,那就敬而遠之吧。