02 尽量以const, enum, inline替换#define
实际是:应该让编译器代替预处理器定义,因为预处理器定义的变量并没有进入到symbol table里面。编译器有时候会看不到预处理器定义
所以用
const double Ratio = 1.653;
来代替
#define Ratio 1.653
当我们以常量替换#defines,有两种特殊情况需要注意。
第一是定义常量指针( constant pointers )。
由于常量定义式通常被放在头文件内(以便被不同的源码含入),因此有必要将指针(而不只是指针所指之物)声明为const。
const char* const authorName = "Scott Meyers";
第二个值得注意的是class专属常量。
为了将常量的作用域(scope)限制于class内,你必须让它成为class的一个成员(member );而为确保此常量至多只有一份实体,你必须让它成为一个static成员:
class GamePlayer{
private:
static const int NumTurns=5; //常量声明式
int scores[NumTurns]; //使用该常量
...
};
另外提供定义式如下:
const int GamePlayer::NumTurns; //NumTurns的定义;
//由于声名式给予了数值,这里就不用了
我们把这个式子放进一个实现文件而非头文件。
如果编译器不支持在class内对static成员在声明式上获得初值,我们可以将初值放在定义式:
class CostEstimate{
private:
static const double FudgeFactor; //static class常量声明
//位于头文件内
};
const double //static class常量定义
CostEstimate::FudgeFactor = 1.35; //位于实现文件内
但这样也有例外,当我们在class编译期间需要一个class常量值,例如在上述的GamePlayer::scores的数组声明式中(是的,编译器坚持必须在编译期间知道数组的大小)。这个时候可以改用所谓的"the enum hack"补偿做法。其理论基础是:“一个属于枚举类型(enumerated type)的数值可权充ints被使用”
class GamePlayer{
private:
enum{NumTurns=5}; //"the enum hack',一令NumTurns
//成为5的一个记号名称.
int scores[NumTurns]; //这就没问题了.
...
};
注:取一个const的地址是合法的,但取一个enum的地址就不合法,而取一个#define的地址通常也不合法。
另一个常见的#define误用情况是以它实现宏(macros ) 。宏看起来像函数,但不会招致函数调用(function call)带来的额外开销。下面这个宏夹带着宏实参,调用函数f:
//以a和b的较大值调用f
#define 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被累加一次
在这里,调用f之前,a的递增次数竟然取决于“它被拿来和谁比较”!
template inline函数可以解决这个问题
template<typename T> //由于我们不知道
inline void callWithMax(const T& a, coast T& b) //T是什么,所以采用
{ //pass by reference-to-coast.
f (a>b?a:b); //pass by reference-to-coast.
} //见条款20.
总结:
1:对于单纯常量,最好以const对象或mums替换#defines。
2:对于形似函数的宏(macros ),最好改用inline函数替换#defines。