为什么c++不提倡使用宏#define

c++
c++

C++ 的书上常说,尽量不要用 #define 来定义常量。这究竟是为什么呢?
其实 C++ 并不仅仅不提倡用宏来定义常量,而且还不提倡用宏来定义“函数”。事实上 C++ 并不是很喜欢预处理宏,在很多很多方面,如果不是必需,尽量不要使用预处理宏。


为什么 C++ 不喜欢预处理宏?
首先,预处理宏是“全局”的。所以,在 C++ 这样如此强调命名空间、类这样的东西的语言中,全局的东西真是越少越好。但是其实预处理宏的全局并不是语义上的全局,之所以叫预处理宏,是因为预处理宏会在编译器编译代码之前被简单地替换成代码。
然后,正因为预处理宏会被简单替换,所以替换的结果是不可预料的。这些说起来还是比较模糊,所以下面将举一些实际的例子。
#define 会把其后的注释也简单地替换

1
2
3
4
5
#define PI 3.14159 // This is a constant.
/* Some harmonious code */
double radius = 2.0;
double area = PI * radius * radius;
cout << area << endl;

某些编译器的结果会输出 3.14159 . 但是使用了 g++ 的测试结果却是正确的。在输出错误结果的编译器下,是因为上面使用了 PI 的那行被替换成了:
double area = 3.14159 // This is a constant. * radius * radius
其实这种情况可以用下面的方法解决:
#define PI 3.14159 /* This is a constant */
尽管可以如此解决,我们还是不提倡使用 #define 定义常量。我们还是有一万个理由,尽量不用 #define .
宏并不能正确地指定类型
我们知道 #define 定义的常量并不需要指定类型,而用 const 修饰的常量是必须指定类型的。这个方面其实没有太多的例子可以举,而且我也没有想出一些明显的错误的例子,所以就提一下吧。
C 里必须用宏定义常数而 C++ 并不一定
考虑以下这段代码:
const int n = 256;
char a[n] = {0};
这段代码如果以 .c 作为后缀保存,会提示定义数组时需要一个常量作下标,因为在 C 语言中,const 只是“不可修改的变量”之意。所以在 C 里只能用 #define 定义常量。但是在 C++ 中却可以用 const.
这些代码在 VC6 和 gcc 中测试过,都是如上所述。
用 #define 定义字面常量可能会浪费很多空间
比如在代码中使用 #define 定义了一个比较长的常量字符串,如果这个宏被使用了很多次,那么这个字面常量将会遍地开花,如果编译器没有那么聪明的话,可能会耗费很多不需要耗费的空间。
用 #define 定义常量对象可能会执行多次构造函数而降低时间效率
参见如下代码:
#define WELCOME_MESSAGE string(“Welcome!”)
如果多次使用 WELCOME_MESSAGE 宏的话,将有可能每次遇到它们的时候都调用 class string 的 string(const char *) 构造函数。这样的话,不仅空间会被浪费,而且也会影响执行效率。#define 定义的“函数”“参数”也只是简单替换
考虑如下代码:

1
2
3
4
5
#define max(a,b) (a > b ? a : b)
/* Some harmonious code */
int x = 5, y = 6;
int n = max(++x, ++y);

这样之后,n 不会是 7. 因为那个调用 max 宏的那行被替换成了:
int n = (++x > ++y ? ++x : ++y);
所以 n 的值会是 8. 其实这种小错误还算不错了,还有更错的。
#define 定义的函数不“认识” C++ 里的 template
当 #define 出生的时候,还没有 template, 似乎也没有 // 开头的注释。考虑如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
#define max(a,b) (a > b ? a : b)
template
class example {
/* ... */
public:
bool operator>(const example &foo) const {
/* ... */
}
};
/* ... */
template x, y;
/* ... */
template n = max(example(x), example(y));

这里的最后一行的 max 宏会把 example(x) 视为 b. 后面的东西就会报错了。在 C 语言里只考虑到了括号内的逗号,而没考虑大小于号里的括号。
但是为什么 C++ 又没有放弃支持 #define
C 语言里有一个 assert.h , 是必须要用宏来实现的。除此之外,C++ 似乎有一种在运行时获取“类”的名字的思路,就是使用宏。还有,宏可以避免头文件被多次包含。而且有的时候需要一些预定义的宏,来控制代码在不同的环境下的编译。
有的时候,对于一些要重复多次,并且比较长的代码,可以在局部启用宏。但是在用完的地方一定要使用 #undef 将其取消。但是大多时候,尽量不要用宏来定义常数和“函数”。
那在 C++ 中应该怎么办才能替代 #define 的一些功能
C++ 不但能替代一些 #define 没有完善的功能,而且能做得更好。对于常数定义,在 C++ 中使用 const 就可以了。对于一些简单的“函数”,可以使用 C++ 的 inline 修饰符,使用这个修饰符,效果上会和一般的函数一样,但是实际上编译器会把函数中的代码根据语义替换到调用函数的地方,所以运行效率不会受到太大影响。而且在使用 max(++i, ++j) 之类的函数的时候,不会是简单替换,所以 ++i 和 ++j 分别只被计算一次。