这个条款或许改为“宁可以编译器替换预处理器”比较好,因为或许#define不被视为语言的一部分。
#define ASPECT_RATIO 1.63
记号名称ASPECT_RATIO也许从未被编译器看见;也许在编译器开始处理源码之前它就被预处理器转移走了。于是记号名称ASPECT_RATIO有可能没进入记号表(sysbol table)内。于是当你运用此常量但获得一个编译错误信息时,可能会带来困惑,因为这个错误信息也许会提到1.653而不是ASPECT_TATIO。如果ASPECT_RATIO被定义在一个非你所写的头文件内,你就不知道它是什么。这个问题也可能出现在记号式调试器中,原因相同:你所使用的名称可能并未进入记号表。
解决之道是以一个常量替换上述的宏:const double AspectTatio = 1.653.
使用常量可能比使用#define导致较小量的代码,因为预处理器“盲目地将宏名称 ASPECT_RATIO替换为1.653”可能导致目标码出现多份1.653,若改用常量AspectRatio绝不会出现相同情况。
当我们以常量替换#define,有两种特殊情况:
一、定义常量指针:由于常量定义式通常被放在头文件内(以便被不同的源码含入),因此有必要将指针(而不只是指针所指之物)声明为const,例如若要在头文件内定义一个常量char*-based字符串,你必须写const两次:const char* const authorName = "Scott Meyers";
eg: const std::string authorName(Scott Meyers);
二、class专属常量:为了将常量的作用域限制于class内,你必须让它成为class的一个成员(member);而为确保此常量至多只有一份实体,你必须让它成为一个static成员:
class GamePlayer{
private:
static const int NumTurns =5;
int scores[NumTurns ];
...
};
只要不取他们的地址,你可以声明并使用它们而无须提供定义式。但如果你取某个class专属常量的地址,或纵使你不取其地址而你的编译器却(不正确的)坚持要看到一个定义式,你就必须另外提供定义式:const int GamePlayer::NumTurns;
把这个式子放进一个实现文件而非头文件。由于class常量在声明时已经获得初始值,因此在定义时不可以再设置初始值。
我们无法利用#defines创建一个class专属常量,因为#defines并不重视作用域,也不提供任何封装性。
另一种写法:
class CostEstimate {
private:
static const double FudgeFactor;
...
};
const double CostEstimate::FudgeFactor = 1.35;