尽量用const和inline而不用#define

  这个条款好称为:“尽量用编译器而不用预处理”,因为#define经常被认为好象不是语言本身的一部分。这是问题之一。再看下面的语句:

  #define ASPECT_RATIO 1.653

  编译器会永远也看不到ASPECT_RATIO这个符号名,因为在源码进入编译器之前,它会被预处理程序去掉,于是ASPECT_RATIO不会加入到符号列表中。如果涉及到这个常量的代码在编译时报错,会很令人费解,因为报错信息指的是1.653,而不是ASPECT_RATIO。如果ASPECT_RATIO不是在你自己写的头文件中定义的,你会奇怪1.653是从哪里来的,甚至会花时间跟踪下去。这个问题也会出现在符号调试器中,因为同样地,你所写的符号名不会出现在符号列表中。

  解决这个问题的方案很简单:不用预处理宏,定义一个常量:


const double ASPECT_RATIO = 1.653;


  这种方法很有效。但有两个特殊情况要注意。

  首先,定义指针常量时会有点不同。因为常量定义一般是放在头文件中(许多源文件会包含它),除了指针所指的类型要定义成const外,重要的是指针也经常要定义成const。例如,要在头文件中定义一个基于char*的字符串常量,你要写两次const:


const char * const authorName = "Scott Meyers";


  关于const的含义和用法,特别是和指针相关联的问题,参见条款21。

  另外,定义某个类(class)的常量一般也很方便,只有一点点不同。要把常量限制在类中,首先要使它成为类的成员;为了保证常量多只有一份拷贝,还要把它定义为静态成员:


class GamePlayer {
private:
 static const int NUM_TURNS = 5; // constant eclaration
 int scores[NUM_TURNS];  // use of constant
 ...
};


  还有一点,正如你看到的,上面的语句是NUM_TURNS的声明,而不是定义,所以你还必须在类的实现代码文件中定义类的静态成员:还有一点,正如你看到的,上面的语句是NUM_TURNS的声明,而不是定义,所以你还必须在类的实现代码文件中定义类的静态成员:


const int GamePlayer::NUM_TURNS; // mandatory definition;
  // goes in class impl.file


  你不必过于担心这种小事。如果你忘了定义,链接器会提醒你。

  旧一点的编译器会不接受这种语法,因为它认为类的静态成员在声明时定义初始值是非法的;而且,类内只允许初始化整数类型(如:int, bool, char 等),还只能是常量。

  在上面的语法不能使用的情况下,可以在定义时赋初值:


class EngineeringConstants { // this goes in the class
private:  // header file
 static const double FUDGE_FACTOR;
 ...
};
 // this goes in the class implementation file
 const double EngineeringConstants::FUDGE_FACTOR = 1.35;


  大多数情况下你只要做这么多。例外的是当你的类在编译时需要用到这个类的常量的情况,例如上面GamePlayer::scores数组的声明(编译过程中编译器一定要知道数组的大小)。所以,为了弥补那些(不正确地)禁止类内进行整型类常量初始化的编译器的不足,可以采用称之为“借用enum”的方法来解决。这种技术很好地利用了当需要int类型时可以使用枚举类型的原则,所以GamePlayer也可以象这样来定义:


class GamePlayer {
private:
 enum { NUM_TURNS = 5 } // "the enum hack" — makes
 // NUM_TURNS a symbolic name
 // for 5
 int scores[NUM_TURNS];// fine
};