尽量少使用宏定义,如果ASPECT_RATIO被定义在一个非你所写的头文件内,为了追踪它会浪费很多时间。作为一个语言常量,AspectRatio肯定会被编译器看到,当然就会进入记号表内。对于浮点常量(比如本例),使用常量可能比使用#define
导致更小量的码,因为预处理器“盲目地将宏名称ASPECT_RATIO替换为1.653”可能导致目标码(object code)出现多份1.653,如果改为常量AspectRatio
绝不会出现相同的情况。
#define ASPECT_RATIO 1.653
//解决方法:
const double AspectRatio = 1.653;
当用const常量替换宏定义,有两种特殊情况:
-
定义常量指针:由于常量定义式通常被放在头文件内,以便被不同的源码含入,因此有必要将指针(不只是指针所指的东西)声明为const。例如要在头文件内定义一个常量的
char*
-based字符串,必须写const两次。const char* const authorName = "Scott Meyers"; //使用string对象更好 const std::string authorName("Scott Meyers");
-
class专属常量:为了把常量的作用域新只在class内部,所以要让这个常量成为类成员。为了确保此常量至多只有一份实体,必须让它成为一个static成员。
class GamePlayer{ private: static const int NumTurns = 5; //常量声明式 int scores[NumTurns]; }
注意,这里看到的是声明式,不是定义式,一般来说都应给出定义式。但是如果这个变量是class专属,而且是static,还为整数类型(ints, chars, bools),只要不取他们的地址,就可以声明并使用它们且不需要提供定义式。如果要取某个class专属常量的地址,就必须提供如下定义式:
//这个式子会放入实现文件而非头文件。 //由于这个常量声明时就有了初值,因此定义时不可以再设初值 const int GamePlayer::NumTurns;
btw, 我们不能用
#define
创建一个class专属常量,因为宏不重视作用域,一旦被宏被定义,在后面的编译过程就有效了,除非在某处用#undef
. 宏不能用来定义class专属常量,也不能提供任何封装性。
用enums代替宏(仍对于单纯常量)
这是const行不通的情况下,一种备用方案。万一编译器(错误的)不允许static整数型class常量完成int class初值设定,就可以用enum hack的做法,因为属于枚举类型的数值可以充当ints被使用。
class GamePlayer{
private:
enum{NumTurns = 5};
int scores[NumTurns];
}
enum hack的几个特点:
- 更类似宏,因为取enum的地址,取
#define
的地址通常都不合法,而取一个const的地址是合法的。 - 不想让别人获得一个指针或者引用指向某个整数常量时,就可以用enum。
- enums和#defines绝对不会导致非必要的内存分配。
- enum hack是模板元编程的基础技术。
用inline函数替代宏定义(形似函数)
对于形似函数的宏,最好用inline函数替代#define
//以a和b的较大值来调用f
#define CALL_WITH_MAX(a,b) f((a)>(b) ? (a) :(b)) //宏的所有实参都要加小括号
//但调用f之前,a的递增次数取决于它被用来和谁比较
int a=5, b=0;
CALL_WITH_MAX(++a,b);
CALL_WITH_MAX(++a, b+10);
//优化:写出template inline函数
//callWithMax是个真正的函数,遵守作用域和访问规则,所以一定可以写出一个类内的私有内联函数。一般,宏无法完成此任务。
template<typename T>
inline void callWithMax(const T& a, const T& b){
f(a>b ? a: b);
}