《Effective C++ 中文版 第三版》读书笔记
条款02:尽量以 const,enum,inline 替换 #define
简单地说就是“宁可用编译器替换预处理器”,因为或许 #define 不被视为语言的一部分。这正是问题所在,例如:
#define ASPECT_RATIO 1.653
记号名称 ASPECT_RATIO 也许未被编译器看见;也许在编译器开始处理源码之前它就被预处理器移走了。于是记号名称 ASPECT_RATIO 有可能没有进入记号表(symbol table)内。于是当你运用此常量但却获得一个编译错误的信息时,可能会带来困惑,因为这个错误信息也许会提到 1.653 而不是 ASPECT_RATIO。如果 ASPECT_RATIO 被定义在一个不是我们自己写的文件内,我们肯定对 1.653 以及它来自何处毫无概念,于是我们将因为追踪它而浪费大量不必要的时间。
解决之道是用常量替换上述的宏(#define):
const double ASPECT_RATIO = 1.653;
作为一个语言常量,ASPECT_RATIO 肯定会被编译器看到,当然就会进入记号表内。此外因为预处理器 “盲目地将宏名称 ASPECT_RATIO 替换为1.653” 可能导致目标码(Object code)出现多份 1.653,若使用常量 ASPECT_RATIO 绝不会出现这种情况。
当我们以常量替换 #define 时,
需要注意的问题一:定义常量指针。
1.若要定义一个常量的(不变的)char*-based 字符串,你必须写 const 两次:
const char* const AuthorName = "Toby";
2.string 对象通常比 char*-based 更好一些,所以上面代码往往写成:
const std::string AuthorName = "Toby";
需要注意的问题二:class 专属常量。
1 为了将常量的作用域限制于class内,你必须让它成为class的一个成员;
2 而为确保此常量至多只有一份实体,你必须让它成为一个static成员。
class Toby {
private:
static const int AuthorAge = 30;
}
基于数个理由 enum hack 值得我们认识。第一,enum hack 的行为某方面说比较像 #define 而不像 const,有时候这正是我们需要的。例如取 const 的地址是合法的,但取一个 enum 的地址就不合法,而取一个 #define 的地址通常也不合法。如果你不想让别人获得一个 pointer 或 reference 指向你的某个整数常量,enum 可以帮你实现这个约束。enum 和 #define 一样绝不会导致非必要的内存分配。
有了 const、enum 和 inline,我们对于处理器(特别是 #define)的需求降低了,但并非完全消除。#include 仍是必需品,而 #ifdef/#ifndef 也继续扮演控制编译的重要角色。目前还不是预处理器全面隐退的时候,但是我们应该明确地将它们压在箱子底尽量不要用它们。
请记住:
1 对于单纯常量,最好以 const 对象或 enum 替换 #define
2 对于形似函数的宏(macro),最好用 inline 函数替换 #define