#define ASPECT_RATIO 1.653
记号名称ASPECT_RATIO可能未被编译器看见,也许在编译器开始处理源码之前就被预处理器移走了,也许获得一个编译错误时候,可能提到1.653而不是ASPECT_RATIO,如果ASPECT_RATIO被定义在一个非你所写的头文件内,那你对1.653已经它来自何处毫无概念,于是你将因为追踪它而浪费时间。
你所使用的名称可能并未进入记号表
解决之道是以一个常量替换宏(#define):
const double AspectRatio = 1.653;
当我们以常量替换#defines时,有两种特殊情况值得说说。
第一是定义常量指针(constant pointers)。由于常量定义式通常被放在头文件内(以便被不同的源码含入),因此有必要将指针(而不只是指针所指物)声明为const。例如若要在头文件内定义一个常量的char*-based字符串,你必须写const两次:
const char* const authorName = "Scott Meyers";
string对象通常比其前辈char*-based合宜,所以上述的authorName往往定义成这样更好些:
const char* const authorName = "Scott Meyers";
第二值得注意的是class专属常量。为了将常量的作用域(scope)限定于class内,你必须让它成为class的一个成员;而为了确保此常量至多只有一份实体,你必须让它成为一个static成员:
- class GamePlayer
- {
- private:
- static const int NumTurns = 5;//常量声明式
- int scores[NumTurns]; //使用该常量
- };
然而你看到的是NumTurns的声明式而非定义式,通常C++要求你对你所使用的任何东西提供一个定义式,但如果它是个class专属常量又是static且为整数类型,则需要特殊处理,只要不取它们的地址,你可以声明并使用它们而无须提供定义式。但如果你取某个class专属常量的地址,或纵使你不取其地址而你的编译器却坚持要看到一个定义式,你就必须另外提供定义式如下:
- const int GamePlayer::NumTurns;//NumTurns的定义
- //由于class常量已经在声明时获得初值,因此定义时不可以再设初值
我们无法利用#define创建一个class专属常量,因为#defines并不重视作用域。#defines也不能够提供任何封装性。
如果你的编译器不支持上述语法,你也可以将初始值放在定义式里:
- class CostEstimate
- {
- private:
- static const double FudgeFactor;//static class 常量声明位于头文内
- //...
- };
- const double CostEstimate::FudgeFactor = 1.35;//static class
- //常量定义位于实现文件内
一个属于枚举类型(enumerated tyoe)的数值可权充ints被使用,于是GamePlayer可定义如下:
- class GamePlayer
- {
- private:
- enum{NumTurns = 5};
- int scores[NumTurns];
- //...
- };
enum hack的行为某方面说比较想#defines而不想const]
- //以a和b的较大值调用f
- #defines 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 callWithMax(const T& a, const T& b)
- {
- f((a)>(b) ? (a) : (b));
- }
有了consts,enums 和inlines 我们对预处理器的需求降低了,当并非完全消除了,#include仍然是必需品,而#ifdef/#ifndef也很重要。
请记住
- 对于单纯常量,最好以const对象或enums替换#defines
- 对于形似函数的宏,最好改用inline函数替换#defines