2011-01-25 26 views
7

我們知道,內聯是有利的,因爲它們由編譯器進行檢查,並且相同的操作(如++ x)在作爲參數傳遞時不會評估多次,宏。宏在C++中的在線優勢

但是在一次採訪中,我被問到宏是否更有利於內聯C++的具體優勢或情況。

有沒有人知道答案或可以對這個問題思考?

回答

14

我能想到的唯一的一些技巧就是你可以用一個無法用內聯函數完成的宏來完成。在編譯期間將令牌粘貼在一起,以及那種駭人聽聞。

+11

自動知道文件名和行號是另一個有用的技巧,只能用宏。 – 2011-01-25 06:27:55

4

有時候你想用其他方法不可能的方式來擴展語言。

#include <iostream> 

#define CHECK(x) if (x); else std::cerr << "CHECK(" #x ") failed!" << std::endl 

int main() { 
    int x = 053; 
    CHECK(x == 42); 
    return 0; 
} 

這打印CHECK(x == 42) failed!

+0

不,它會在`return 0`行發出編譯錯誤 - 缺少分號。並且可能會警告`else`控制一個空語句。 – 2011-01-25 06:26:48

+0

@Ben:固定!不會在我的GCC中發出任何警告。 – ephemient 2011-01-25 06:28:42

+0

如果你想讓你的宏後面跟一個分號,使用`do {...} while(0)`。你當前的宏允許像'CHECK(0)<< 5;` – 2011-01-25 06:30:45

8

這裏是一個特定的情況,宏不僅是首選,它們實際上是唯一的方法來完成某件事情。

如果你想要寫一個記錄不僅一些消息,但實例發生在該文件&行號記錄功能,您可以直接打電話給你的功能,在文件&線值(或宏)打字直接:

LogError("Something Bad!", __FILE__, __LINE__); 

...或者,如果你想讓它自動工作,你必須依賴於一個宏(警告:不編譯):

#define LogErrorEx(ERR) (LogError(ERR, __FILE__, __LINE__)) 
// ... 
LogErrorEx("Something Else Bad!"); 

這不能用模板來實現,默認參數,默認構造或C++中的任何其他設備。

+0

對於日誌記錄來說, `語句來檢查是否啓用了特定級別的日誌記錄。如果爲false,則記錄參數根本不會被評估。 – 2011-01-25 12:45:18

0

宏就像文本替換定義。

是進入我的腦海裏,這些本質上的區別是:

  • 它不能是函數等。例如,我的意思是它不一定包含一些一致的括號。
  • 它可以在別處使用。就像在類聲明範圍中一樣,甚至在全局範圍內。所以它不能在另一個功能的範圍內。

如果要執行是無法使用的功能進行操作您必須使用它們:

  • 初始化複雜的表(讓核心更易讀)
  • 一些特殊成員一樣的方便聲明事件ID或標記類(在MFC IMPLEMENT_DYNAMIC中使用很多)
  • 在函數開頭擠壓重複聲明
  • 已提及的使用__LINE____FILE__,...用於日誌記錄
1

在C++中,特別是,似乎很常彈出的MACRO的一種用法(除了帶文件和行的調試打印外)是使用MACRO在類中填充一組標準方法不能從基類繼承。在一些創建RTTI,序列化,表達式模板等自定義機制的庫中,它們通常依賴於一組靜態常量變量和靜態方法(並且對於一些不能被繼承的重載操作符可能是特殊的語義),這幾乎總是相同但需要添加到用戶在此框架內定義的任何類。在這些情況下,通常提供MACRO,使得用戶不必擔心放置所有必要的代碼(他只需要用要求信息調用MACRO)。舉例來說,如果我做一個簡單的RTTI(運行時類型識別)系統,我可能會要求所有的類都具有TYPEID並動態澆注料:

class Foo : public Bar { 
    MY_RTTI_REGISTER_CLASS(Foo, Bar, 0xBAADF00D) 
}; 

#define MY_RTTI_REGISTER_CLASS(CLASSNAME,BASECLASS,UNIQUEID) \ 
    public:\ 
    static const int TypeID = UNIQUEID;\ 
    virtual void* CastTo(int aTypeID) {\ 
     if(aTypeID == TypeID)\ 
     return this;\ 
     else\ 
     return BASECLASS::CastTo(aTypeID);\ 
    }; 

以上不能使用模板或繼承來完成,它使用戶的生活更輕鬆,避免代碼重複。

我會說這種MACROs的使用是迄今爲止最常見的C++。

-1

我想補充兩個用途:

  1. MINMAX,直到的C++ 0x,因爲返回類型必須由手工申報,混合minmax作爲內聯函數將是噩夢般的,而一個簡單的宏在眨眼間做了。
  2. 隱私:您可以隨時在undef宏之前退出您的標題,您不能「undeclare」一個內聯函數(或另一個符號)。這是由於C和C++語言缺乏適當的模塊化。
1

前面已經說過,宏可以使用預處理程序指令:__FILE____LINE__例如,當然#include#define也可以是參數的行爲非常有用:

#ifdef __DEBUG__ 
# define LOG(s) std:cout << s << std:endl 
#else 
# define LOG(s) 
#endif 

根據閹__DEBUG__定義或不(通過#define或通過編譯器選項),LOG宏將被激活或不激活。這是一種簡單的方法,讓代碼中的任何地方都可以輕鬆地取消激活調試信息。

你也可以考慮改變分配內存的方式(malloc將被重新定義爲以一個內存池而不是標準堆爲例,等等)。

1

正如名稱所示,內聯函數僅限於功能性任務,執行某些代碼。

宏具有更廣泛的應用程序,它們可能擴展到例如聲明或替換整個語言結構。一些實例中(對於C和C++編寫的),它可以不與功能來實現:

typedef struct POD { double a; unsigned b } POD; 
#declare POD_INITIALIZER { 1.0, 37u } 

POD myPOD = POD_INITIALIZER; 

#define DIFFICULT_CASE(X) case (X)+2 :; case (X)+3 
#define EASY_CASE(X) case (X)+4 :; case (X)+5 

switch (a) { 
    default: ++a; break; 
    EASY_CASE('0'): --a; break; 
    DIFFICULT_CASE('A'): a = helperfunction(a); break; 
} 

#define PRINT_VALUE(X)      \ 
do {           \ 
char const* _form = #X " has value 0x%lx\n"; \ 
fprintf(stderr, _form, (unsigned long)(X)); \ 
} while (false) 

在C++中的上下文中,加速有很多的更多的例子是更復雜和更實用。但是因爲對於這種宏,你在某種程度上擴展了語言(不是嚴格意義上,預處理器是其中的一部分),許多人不喜歡宏,特別是在C++社區中,在C社區中少一點。

在任何情況下,如果您使用這樣的構造,您應該非常清楚應該實現什麼,如何編寫文檔以及如何抵制混淆代碼的誘惑。

0
#include <stdio.h> 
    #define sq(x) x*x 
    int main() 
    { 
     printf("%d", sq(2+1)); 
     printf("%d", sq(2+5)); 
     return 0; 
    } 

該代碼的輸出是5和17.宏以文本方式擴展。它不像功能。

解釋在這個例子中:

平方(2 + 1)= 2 + 1 * 2 + 1 = 2 + 2 + 1 = 5

平方(2 + 5)= 2 + 5 * 2 + 5 = 2 + 10 + 5 = 17