但是在面试中,有人问到宏定义在什么情况下比内联函数更为优越或更加适用于C++。
有没有人知道答案或对这个问题有想法?
我所能想到的唯一一件事是,有些宏定义技巧无法通过内联函数实现。这包括在编译时将标记粘贴在一起等技巧。
在某些特定情况下,宏不仅是首选,实际上也是唯一的方法来完成某些任务。
如果您想编写一个记录函数,该函数不仅记录某些消息,还要记录发生事件的文件和行号,则可以直接调用该函数,并直接输入文件和行值(或宏):
LogError("Something Bad!", __FILE__, __LINE__);
如果您希望它自动工作,您必须依赖于宏(警告:未编译):
#define LogErrorEx(ERR) (LogError(ERR, __FILE__, __LINE__))
// ...
LogErrorEx("Something Else Bad!");
在C++中,这不能通过模板、默认参数、默认构造或任何其他方式实现。
if
语句来检查是否启用了特定级别的日志记录。当为假时,不会对记录日志的参数进行评估。 - edA-qa mort-ora-y#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!
。
return 0
这一行上会出现编译错误——缺少分号。而且可能会有一个警告,指出else
控制了一个空语句。 - Ben Voigtdo { ... } while (0)
。你当前的宏定义允许像 CHECK(0) << 5;
这样的东西。 - Ben VoigtCHECK
后面添加更多的诊断信息(例如:CHECK(foo) << "this is @j_random_hacker's fault" << std::endl;
)。 - ephemientclass 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);\
};
以上内容无法使用模板或继承完成,它使用户的生活更轻松,避免了代码重复。
我认为这种宏的使用方式在C++中是最常见的。
正如之前提到的那样,宏可以使用预处理指令:__FILE__
和__LINE__
等。当然,#include
和#define
也是有用的,可以用来参数化行为。
#ifdef __DEBUG__
# define LOG(s) std:cout << s << std:endl
#else
# define LOG(s)
#endif
根据 __DEBUG__
是否被定义(通过 #define
或编译器选项),LOG
宏将会激活或不激活。这是一种简单的方式,在代码中随处添加调试信息并可以轻松地停用。
您还可以考虑更改内存分配方式(例如,将 malloc
重新定义为针对内存池而不是标准堆等)。
#ifdef __DEBUG__ -- inline void log() { ... } -- #else -- inline void log() {} -- #endif
? - Sebastian Mach内联函数,顾名思义,仅限于函数任务,执行一些代码。
宏有更广泛的应用,例如可以扩展到声明或替换整个语言结构。以下是一些(针对 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++的语境下,Boost有更多更复杂、更实用的示例。
但是,由于这些宏实际上是扩展了语言(虽然预处理器也是语言的一部分),许多人都不喜欢使用宏,特别是C++社区中,而在C社区中则稍微好一些。
无论如何,如果您使用这样的结构,您应该始终非常清楚地知道它们的目的,进行充分的文档记录,并抵制混淆代码的诱惑。
#include <stdio.h>
#define sq(x) x*x
int main()
{
printf("%d", sq(2+1));
printf("%d", sq(2+5));
return 0;
}
宏定义就像文本替换一样。
我能想到的基本区别有:
如果要执行无法使用函数执行的操作,则必须使用宏定义:
__LINE__
,__FILE__
,...我会添加两个用途:
MIN
和 MAX
,直到 C++0x,因为返回类型必须手动声明,混合使用 min
和 max
作为内联函数将是一场噩梦,而一个简单的宏可以在眨眼间完成。std::min
和std::max
作为模板函数可以很好地工作。 - edA-qa mort-ora-ystd::min
版本(在C++0x之前)是一种沮丧的练习(如果我记得正确,需要大约一百行代码),而使用宏,编译器可以轻松地处理整数提升。 - Matthieu M.