条款 02:尽量以 const,enum,inline 替换 #define
Prefer consts,enums,and inlines to #defines.
宁可以编译器替换预处理器
#define ASPECT_RATIO 1.653
也许 ASPECT_RATIO 不会被编译器感知,即可能提前被预处理器移走了,导致 ASPECT_RATIO 可能没进入记号表(symbol table)中。当遇到编译错误时,可能编译器提示 1.653 而非 ASPECT_RATIO,增加排错时间。在记号式调试(symbolic debugger)中也会有相同问题。因此可以用一个常量替换上述宏。
const double AspectRaito = 1.653;
AspectRaito 作为语言常量一定会进入记号表。而且可能会比 #define 生成较小量的码。预处理器盲目替换可能导致目标码(object code)出现多份 1.653。
定义常量指针
const char* const name= "zf";
通常使用 string 对象较合宜
const std::string name ("zf");
class 中定义常量
class GamePlayer
{
private:
static const int NumTurns = 5; // 常量式申明
int Scores[NumTurns]; // 使用该常量
};
上面是声明式而非定义式,如果编译器需要一个定义式时,需要提供如下定义式:
const int GamePlayer::NumTurns; // NumTurns 的定义
define 无法创建一个 class 专属变量,因为它不重视作用域(scope),所以也不能提供任何封装性
enum hack
旧式编译器可能会要求将初值赋在定义式
class GamePlayer
{
private:
static const int NumTurns; // 申明在头文件
};
const int GamePlayer::NumTurns = 5; // 定义在实现文件
如果编译器需要在编译期间就知道一个 class 的常量值,如上述 Scores[NumTurns] 数组就要求知道 NumTurns 大小,而编译器又不支持在声明式中赋初值时,可以改用 “the enum hack” 做法
class GamePlayer
{
private:
enum { NumTurns = 5 };
int Scores[NumTurns];
};
其理论基础是,enum 可以被充当 int 使用,需要熟悉 enum hack 的理由:
- enum hack 更像 #define,例如取 const 变量地址是合法的,而去 enum 的地址就不合法,有时这正是想要的,可以避免别人用 pointer 或者 reference 指向你的整数常量
- 不够优秀的编译器可能会给 const 整数变量分配不必要的内存空间,而 enum 和 #define 则一定不会
- 使用广泛,而且 enum hack 是模板元编程(template metaprograming)的基础技术
使用 inline 替代 #define
可以带来宏的效率以及类型安全性
#define CALL_WITH_MAX (a, b) f ((a) > (b) ? : (a), (b))
int a = 5, b = 0;
CALL_WITH_MAX (++a, b); // a 被累加一次
CALL_WITH_MAX (++a, b+10); // a 被累加二次
即使全部加上括号,也可能会有非预期的行为发生。template inline 可以避免这些问题
template <typename T>
inline void CALL_WITH_MAX (const T&a, const T& b)
{
f(a > b ? a : b);
}
而且 inline 遵守作用域(scope),完全可以实现 class private inline