条款 02:尽量以 const,enum,inline 替换 #define

Prefer consts,enums,and inlines to #defines.

宁可以编译器替换预处理器

  1. #define ASPECT_RATIO 1.653

也许 ASPECT_RATIO 不会被编译器感知,即可能提前被预处理器移走了,导致 ASPECT_RATIO 可能没进入记号表(symbol table)中。当遇到编译错误时,可能编译器提示 1.653 而非 ASPECT_RATIO,增加排错时间。在记号式调试(symbolic debugger)中也会有相同问题。因此可以用一个常量替换上述宏。

  1. const double AspectRaito = 1.653;

AspectRaito 作为语言常量一定会进入记号表。而且可能会比 #define 生成较小量的码。预处理器盲目替换可能导致目标码(object code)出现多份 1.653。

定义常量指针

  1. const char* const name= "zf";

通常使用 string 对象较合宜

  1. const std::string name ("zf");

class 中定义常量

  1. class GamePlayer
  2. {
  3. private:
  4. static const int NumTurns = 5; // 常量式申明
  5. int Scores[NumTurns]; // 使用该常量
  6. };

上面是声明式而非定义式,如果编译器需要一个定义式时,需要提供如下定义式:

  1. const int GamePlayer::NumTurns; // NumTurns 的定义

define 无法创建一个 class 专属变量,因为它不重视作用域(scope),所以也不能提供任何封装性

enum hack

旧式编译器可能会要求将初值赋在定义式

  1. class GamePlayer
  2. {
  3. private:
  4. static const int NumTurns; // 申明在头文件
  5. };
  6. const int GamePlayer::NumTurns = 5; // 定义在实现文件

如果编译器需要在编译期间就知道一个 class 的常量值,如上述 Scores[NumTurns] 数组就要求知道 NumTurns 大小,而编译器又不支持在声明式中赋初值时,可以改用 “the enum hack” 做法

  1. class GamePlayer
  2. {
  3. private:
  4. enum { NumTurns = 5 };
  5. int Scores[NumTurns];
  6. };

其理论基础是,enum 可以被充当 int 使用,需要熟悉 enum hack 的理由:

  • enum hack 更像 #define,例如取 const 变量地址是合法的,而去 enum 的地址就不合法,有时这正是想要的,可以避免别人用 pointer 或者 reference 指向你的整数常量
  • 不够优秀的编译器可能会给 const 整数变量分配不必要的内存空间,而 enum 和 #define 则一定不会
  • 使用广泛,而且 enum hack 是模板元编程(template metaprograming)的基础技术

使用 inline 替代 #define

可以带来宏的效率以及类型安全性

  1. #define CALL_WITH_MAX (a, b) f ((a) > (b) ? : (a), (b))
  2. int a = 5, b = 0;
  3. CALL_WITH_MAX (++a, b); // a 被累加一次
  4. CALL_WITH_MAX (++a, b+10); // a 被累加二次

即使全部加上括号,也可能会有非预期的行为发生。template inline 可以避免这些问题

  1. template <typename T>
  2. inline void CALL_WITH_MAX (const T&a, const T& b)
  3. {
  4. f(a > b ? a : b);
  5. }

而且 inline 遵守作用域(scope),完全可以实现 class private inline