写C++代码时,你可能会写下这样的语句
#define ASPECT_RATIO 1.653
而记号名称 ASPECT_RATIO 编译器可能从未看见;因为可能在编译器开始处理源码之前就被预处理器移走了。于是记号名称 ASPECT_RATIO 有可能没有进入符号表(symbol table)内。于是当你运用此常量但获得了一个编译错误信息时,可能会带来困惑:这个编译错误信息提到是1.653而不是 ASPECT_RATIO 。如果 ASPECT_RATIO 被定义在一个非你所写的头文件中,你肯定对1.653以及它来自哪里毫无概念,于是你将因为追踪它浪费时间。
解决的方法是用一个常量替换上述的#define:
const double AspectRatio = 1.653; // 大写名称通常用于宏
//因此这里改变名称写法
作为一个语言常量,AspectRatio肯定会进入符号表,因此当出现编译错误时,就不会出现和使用#define相同的情况。
以const替换#define时,有两种特殊情况值得说说:
第一是定义常量指针,由于常量定义式通常被放在头文件中,因此有必要将指针声明为const。例如要在头文件内定义一个常量的char *字符串,你必须写const两次:
const char* const authorName = "Scott Meyers";
const std::string authorName("Scott Meyers");
class GamePlayer{
private:
static const int NumTurns = 5;
int scores[NumTurns];
}
(注:类里面的静态成员只会被声明一次,就是说,无论你声明了多少个这个类的对象,他们都共享一个静态成员,静态成员函数也是一样的道理,且静态成员函数只能够访问静态成员变量,即,静态成员函数并不具有普通成员函数具有的this指针,而this指针就是成员函数能够访问类中非静态成员的原因,且,除了类对象能够调用静态成员函数以外,类本身也可以,而非静态成员函数可以任意地访问静态成员函数和静态数据成员。只有静态的常量整形数据成员才可以在类定义中初始化,非常量的静态数据成员及非整形的常量都必须在类体外初始化)
如果你的编译器坚持要看到一个你所声明的静态整形成员变量的定义式,你就把这个式子放入一个实现文件而非头文件。
const int GamerPlayer::NumTurns;
由于该常量在class中声明时已经获得初值,因此定义时不可以再设置初值。
顺带一提,我们无法利用#define创建一个class专属常量,因为#define不重视作用域。一旦宏被定义,它在其后的编译过程中就有效(除非在某处被#undef)。这就意味#define不仅不能够用来定义class专属常量,也不能提供任何封装性,也就是说没有所谓private #define这样的东西。而当然const成员变量是可以被封装的,NumTurns就是。
如果你的编译器不允许static成员在声明式上获得初始值(当然,此处的初始值也仅仅指整数常量),你可以把初值放在定义式
class CostEstimate{
private:
static const double FudgeFactor;
}
const double CostEstimate::FudgeFactor=1.35;
这样做的唯一例外是当你在class编译期间需要一个class常量值,例如在上述的GamePlayer::scores的数组声明式中(是的,编译器坚持编译期间必须知道数组的大小)。这时候万一你的编译器不予许static成员在声明式上获得初始值,可以改用枚举常量的方法,即enum hack。
class GamePlayer{
private:
enum {NumTurns=5};
int scores[NumTurns];
}
enum hack的行为的行为比较像#define而不是const,例如取一个const的地址是合法的,但取一个enum的地址是不合法的,而取#define的地址也是不合法的。
对#define的另一种常见误用是以他实现宏,宏看起来像函数,但不会招致函数调用带来的额外开销,下面这个宏夹带着宏实参,调用函数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被累加2次
CALL_WITH_MAX(++a,b+10); //a被累加1次
在这里,调用f之前,a的递增次数竟然取决于它被用来和谁比较! 宏定义只是简单的字符替换,因此使用内联函数是更好的做法。
template
inline void callWithMax(const T&a,const T&b)
{
f(a>b)? a:b;
}