我们知道内联是有利的,因为它们被编译器检查,并且与宏相比,当作为参数传递时,相同的操作(如++ x)不会评估多次。
但是在一次采访中,我被问到当宏更有利于C ++内联时的具体优势或情况。
有没有人知道答案或者可以对这个问题进行思考?
答案 0 :(得分:14)
我唯一能想到的是你可以使用内联函数无法完成的宏做一些技巧。在编译时将令牌粘贴在一起,以及那种hackery。
答案 1 :(得分:8)
这是一个特殊情况,宏不仅是首选,它们实际上是实现某些目标的唯一方法。
如果你想编写一个记录功能,它不仅记录某些消息,而且记录文件&实例发生的行号,你可以直接调用你的函数,输入文件&线值(或宏)直接:
LogError("Something Bad!", __FILE__, __LINE__);
...或者,如果您希望它自动运行,您必须依赖宏(警告:未编译):
#define LogErrorEx(ERR) (LogError(ERR, __FILE__, __LINE__))
// ...
LogErrorEx("Something Else Bad!");
使用模板,默认参数,默认构造或C ++中的任何其他设备无法实现。
答案 2 :(得分: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!
。
答案 3 :(得分:1)
在C ++中,一个似乎经常弹出的MACRO的一种用法(除了带有文件和行的调试打印)是使用MACRO来填充一个不能从a继承的类中的一组标准方法基类。在一些创建RTTI,序列化,表达式模板等自定义机制的库中,它们通常依赖于一组静态const变量和静态方法(对于一些无法继承的重载运算符,可能还有特殊的语义),它们几乎总是相同但需要添加到用户在此框架中定义的任何类。在这些情况下,通常提供MACRO,使得用户不必担心放入所有必需的代码(他只需要使用require信息调用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);\
};
上述功能无法通过模板或继承来完成,它使用户的生活更轻松,避免了代码重复。
我想说这种MACRO的使用是迄今为止在C ++中最常见的。
答案 4 :(得分:1)
如上所述,宏可以使用预处理器指令:__FILE__
,__LINE__
,但当然#include
和#define
对参数行为也很有用:< / p>
#ifdef __DEBUG__
# define LOG(s) std:cout << s << std:endl
#else
# define LOG(s)
#endif
根据是否定义了__DEBUG__
(通过#define
或通过编译器选项),LOG
宏将处于活动状态。这是一种在代码中随处可以调试信息的简单方法,可以轻松取消激活。
您还可以考虑更改内存的分配方式(malloc
将重新定义为内存池,而不是标准堆,例如......)。
答案 5 :(得分: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 ++的上下文中,Boost有很多更多参与且更有用的例子。
但是因为有了这样的宏,你在某种程度上扩展了语言(严格来说,预处理器是其中的一部分),许多人不喜欢宏,特别是在C ++社区中,在C社区中少一点。
在任何情况下,如果你使用这样的结构,你应该总是非常清楚应该达到的目标,记录好,并反对混淆代码的诱惑。
答案 6 :(得分:0)
宏就像文本替换定义一样。
我想到的这些本质区别是:
如果要执行无法使用功能执行的操作,则必须使用它们:
__LINE__
,__FILE__
,...进行记录答案 7 :(得分: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.宏以文本方式展开。它不像功能。
此示例的说明:
sq(2 + 1)= 2 + 1 * 2 + 1 = 2 + 2 + 1 = 5
sq(2 + 5)= 2 + 5 * 2 + 5 = 2 + 10 + 5 = 17
答案 8 :(得分:-1)
我会添加两种用途:
MIN
和MAX
,直到C ++ 0x,因为必须手动声明返回类型,混合min
和max
,因为内联函数本来是噩梦,虽然一个简单的宏在眨眼间就做到了。undef
宏,您不能“取消声明”内联函数(或其他符号)。这是由于C和C ++语言缺乏适当的模块化。