条款02:尽量以const,enum,inline替换 #define
1、条款改为宁愿用编译器也不用预处理器, 因为#define不被视为语言的一部分,所以就来了问题。
2、#define ASPECT_RATIO 1.653
这个宏ASPECT_RATIO不被编译器所见,是在被预处理器处理掉了,所以这个名称有可能没有进入记号表内。当运行此变量但是获得一个编译错误时,这个错误会提到1.653而不是这个变量,如果这个变量被定义在别人所写的头文件里,你可能会浪费时间。原因就是所使用的的名称未进入记号表中。
还是用常亮来替换上面的宏:
const double AspectRatio = 1.653;
作为一个语言常亮,AspectRatio肯定会被编译器处理,就会进入记号表内,此外对浮点常量来说,使用常量可能比使用宏导致较小的码,因为预处理器将宏替换可能导致目标码出现多份。
当我们以常量来替换宏,有两种特殊情况值得说说。第一是定义常量指针。我们通常将常量定义式放在头文件中,因此有必要将指针(而不只是指针所指之物)声明为const。例如若在头文件内定义一个常量的(不变的)字符串,就要写成:const char* const authorName = “yy”;
const与指针的使用会在条款3中完整叙述,所以上述定义成const std::string authorName(“yy”);会好一些。
第二个值得注意的是class专属常量。为了将常量的作用域限制在class内,必须将它成为class的一个成员;而为确保此常量至多只有一分实体,必须让其成为一个static成员:
class GamePlayer
{
private:
static const int num = 5;
int scores[num];
};
你所看到的是num的声明式而非定义式。通常C++要求所使用的的任何东西提供一个定义式,但是如果他是个class专属常量又是static且为整数类型,则需要特殊处理。只要不取她们的地址,,就可以声明并使用他们而无需提供定义式,但是你取某个class专属常量的地址,或纵使你不取其他地址而你的编译器却(不正确)坚持要看到定义式,你就必须另外提供定义式:const int GamePlayer::num;
请把这个式子放进一个实现文件而非头文件。由于class常量已在声明时获得初值(例如先前声明num时为5),因此定义时不可以再设初值。注意: 我们无法利用#define创建一个class专属常量,因为#define并不重视作用域,一旦宏被定义,它就在其后的编译器过程中有效。这意味着#define不仅不能用来定义class专属常量,也不能提供任何封装性。
旧式编译器也许不支持上述语法,他们不允许static成员在其声明上获得初值,此外所谓的"in-class初值设定"也只允许对整数常量进行。如果你的编译器不支持上述语法,你可以将初值放在定义式内。
这时候当你在class编译期间需要一个class常量值,像上述的GamePlayer::scores的数组声明中(是的,编译器坚持必须在编译期间知道数组的大小),这时候万一你的编译器不予许"static整数型class常量"完成在类中初值设定,可以改用所谓的“the enum hack”做法,定义如下:
class GamePlayer
{
private:
enum{num = 5};
int scores[num];
};
基于整个理由enum hack值得我们认识。如果你不想让别人获得指针或者引用指向你的整数常量,enum可以帮你实现这个约束。Enums和#defines不会导致非必要的内存分配。
虽然宏的缺点太多,有了const,enum,inline,我们对预处理器的需求降低了,但并非完全消除,#include仍然是必需品.
记住:对于单纯常量,最好以const对象或enum替换define。
对于形似函数的宏,最好改用inline函数替换define.