可能重复:
When are C++ macros beneficial?
Why is #define bad and what is the proper substitute?
有人告诉我#define很糟糕。好吧,老实说我不明白为什么不好。如果它不好,那么我可以用其他方式做到这一点呢?
#include <iostream>
#define stop() cin.ignore(numeric_limits<streamsize>::max(), '\n');
答案 0 :(得分:6)
#define
本身并非错误。但是,通常有更好的方式来做你想做的事情。考虑inline
函数:
inline void stop() {
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
(真的,你甚至不需要inline
这样的功能。只需一个简单的普通功能就可以了。)
答案 1 :(得分:3)
这很糟糕,因为它不分青红皂白。您在代码中停止()的任何地方都将被替换。
解决问题的方法是将代码放入自己的方法中。
答案 2 :(得分:3)
它不一定是坏,只是人们过去使用它的大多数事情都可以用更好的方式完成。
例如,您提供的代码段(和其他代码宏)可能是内联函数,类似于(未经测试):
static inline void stop (void) {
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
此外,还有其他一些代码宏迫使你做“宏体操”的事情,比如如果你想打电话给写得很糟糕的话:
#define f(x) x * x * x + x
使用:
int y = f (a + 1); // a + 1 * a + 1 * a + 1 + a + 1 (4a+2, not a^3+a)
int z = f (a++); // a++ * a++ * a++ + a++
由于操作符的优先级,第一个将完全让您惊讶于结果,第二个将给您未定义的行为。内联函数不会遇到这些问题。
宏的另一个主要用途是提供枚举值,例如:
#define ERR_OK 0
#define ERR_ARG 1
: :
#define ERR_MEM 99
使用枚举更好地完成这些工作。
宏的主要问题是替换是在翻译阶段的早期完成的,因此信息经常丢失。例如,调试器通常不知道ERR_ARG
,因为它会在创建调试信息的转换过程部分之前很久才被替换。
但是,对他们进行了足够的诽谤,他们仍然用于定义可用于条件编译的简单变量。这就是我现在在C ++中使用它们的全部内容。
答案 3 :(得分:3)
在C ++中,使用#define
并不是强制性的,尽管应该首选替代方案。有一些上下文,例如include guards,其中没有其他便携式/标准替代方案。
应该避免这种情况,因为C preprocessor在编译器之前运行(顾名思义)。它执行简单的文本替换,而不考虑其他定义。这意味着输入到编译器的结果有时没有意义。考虑:
// in some header file.
#define FOO 5
// in some source file.
int main ()
{
// pre-compiles to: "int 5 = 2;"
// the compiler will vomit a weird compiler error.
int FOO = 2;
}
这个例子似乎微不足道,但存在真实的例子。某些Windows SDK标头定义:
#define min(a,b) ((a<b)?(a):(b))
然后代码如下:
#include <Windows.h>
#include <algorithm>
int main ()
{
// pre-compiles to: "int i = std::((1<2)?(1):(2));"
// the compiler will vomit a weird compiler error.
int i = std::min(1, 2);
}
当有替代品时,请使用它们。在发布的示例中,您可以轻松地编写:
void stop() {
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
对于常量,使用真正的C ++常量:
// instead of
#define FOO 5
// prefer
static const int FOO = 5;
这将保证您的编译器看到与相同的事情,并且在嵌套作用域中使用名称覆盖使您受益(本地FOO
变量将覆盖全局{{1}的含义如预期的那样。
答案 4 :(得分:2)
#define
本身并不坏,但确实有一些不好的属性。我将列出一些我所知道的事情:
“功能”未按预期运行。
以下代码似乎合理:
#define getmax(a,b) (a > b ? a : b)
...但如果我这样称呼它会发生什么?:
int a = 5;
int b = 2;
int c = getmax(++a,b); // c equals 7.
不,这不是拼写错误。 c
将等于7.如果您不相信我,请尝试一下。仅此一点就足以吓唬你了。
预处理器具有固有的全局性
每当您使用#define
定义一个函数(例如stop()
)时,它会在被发现后在所有包含的文件中起作用。
这意味着您实际上可以更改未编写的库。只要他们在头文件中使用函数stop()
,您就可以更改未编写且未修改的代码的行为。
调试更加困难。
预处理器在代码进入编译器之前进行符号替换。因此,如果您有以下代码:
#define NUM_CUSTOMERS 10
#define PRICE_PER_CUSTOMER 1.10
...
double something = NUM_CUSTOMERS * PRICE_PER_CUSTOMER;
如果该行有错误,那么您将不会在错误消息中看到方便的变量名称,而是会看到如下内容:
double something = 10 * 1.10;
这使得在代码中查找内容变得更加困难。在这个例子中,它看起来并不那么糟糕,但如果你真的养成了这样做的习惯,那么你可能会遇到一些真正的麻烦。