Prefer consts, enums, and inlines to #defines
#define
#define ASPECT_RATIO 1.653
#define不被视为语言的一部分,其后的符号没有在记号表(symbol table)内。由预处理器处理,编译时产生的错误不能很好追踪。
解决之道是以一个常量替换上述的宏:
const double AspectRatio = 1.653
对浮点常量而言,使用常量可能比使用#define产生较小量的码,预处理器“盲目地将宏名称ASPECT_RATIO替换为1.653”,此举可能导致目标码出现多份1.653,若改用常量AspectRatio绝不会出现相同的情况。
class专属常量
class GamePlayer {
private:
static const int m_NumTurns = 5; // 常量声明式
int scores[m_NumTurns];
static const double m_Factor; // 常量声明,位于头文件内(.h中)
...
};
const int GamePlayer::m_NumTurns; // 常量定义,位于实现文件内(.cpp中)
const double GamePlayer::m_Factor = 1.35;
上面是NumTurns的声明式而非定义式。所谓的“in-class初值设定”也只允许对 整数常量 进行。有些编译器不允许“static整数型class常量”完成“in class初值设定”,可改用所谓的“the enum hack”补偿做法。其理论基础是:“一个属于枚举类型的数值可充当int类型被使用”,于是上面GamePlayer可定义如下:
class GamePlayer {
private:
enum { m_NumTurns = 5; } // the enum hack
int scores[m_NumTurns];
...
};
从某方面说enum hack的行为比较像#define而不像const,有时候这正是你想要的。例如取一个const的地址是合法的,但取一个enum的地址就不合法,而取一个#define的地址也通常也不合法。如果你不想让别人获得一个pointer或reference指向你的某个整数常量,enum可以帮助你实现这个约束。
带参数宏定义的误用
// 以a和b的较大值调用函数f
#define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b))
宏看起来像函数,且不会招致函数调用带来的额外开销。但这般长相的宏有着太多的缺点,纵使你记得为所有实参加上小括号,不信看看下面不可思议的代码:
int a = 5;
int b = 0;
CALL_WITH_MAX(++a, b); // a被累加两次
CALL_WITH_MAX(++a, b + 10); // a被累加一次
这这里,调用f之前,a的递增次数竟然取决于“它被拿来和谁比较”!
幸运的是我们有template inline函数,不仅有宏的效率还有同一般函数一样的所有可预料行为和类型安全性:
template<typename T>
inline void callWithMax(const T& a, const T& b)
{
f(a > b ? a : b);
}
总结
- 对于单纯的常量,最好以const对象或enum替换#define
- 对于形似函数的宏,最好改用inline函数替换#define