条款2 尽量以const,enum,inline 替换#define

这个条款可以理解为“以编译器替换预处理器”

由于预处理器(如#define等)并不被视为语言的一部分,所以让你

#define ASPECT_RATIO 1.653

这样定义时,标记名称ASPECT_RATIO或许从未被编译器看见,所以如果这样运行时出错,编译错误信息往往会提到1.653,如果标记名称ASPECT_RATIO在一个并非你写的头文件里,就会为了追踪它在哪里而浪费时间。

原因:你所使用的名称可能并未进入记号表(symbol table)

解决方法:用一个常量替换上述的宏#define:

const double AspectRatio=1.652;//大写名称通常用于宏,这里改变写法

语言常量AspectRatio一定会被编译器看到,因此会进入记号表

此外:对于浮点常量(就如上例),使用常量可能比使用#define导致较小量的码。因为预处理器“盲目的将宏名称ASPECT_RATIO替换为1.653”可能导致目标码内出现多份1.653

以常量替换#define时,两种特殊情况

一:定义常量指针:

​ 由于常量定义式通常被放在头文件(方便被不同的源码含入),所以要把指针(并不是指针所指之物)声明为const。

例如:如果要在头文件定义一个常量(不变的)char*-based字符串,必须写const两次

const char* const authorName="Scott Meyers";

但是通常string对象比其前辈char*-based更合适。所以往往可以这样定义

const std::string authorName("Scott Meyers");

二:class专属常量。

​ 为了将常量的作用域(scope)限制于class内,必须让它成为class的一个成员(member);而为了确保此常量最多有一份,必须让它成为一个static成员:

class GamePlayer{
    private:
    	static const int NumTurns=5;//常量声明式
    	int scores[NumTurns];		//使用该常量
    ...
};

这里的NumTurns是声明式,而非定义式。

通常C++要求你对使用的任何东西提供一个定义式。但如果它是个class专属常量又是static且为整数类型(intergral type;例如ints,chars,bools),就需要特殊处理。只要不取它们的地址。就可以声明并使用而无需提供定义式。

如果你要取其地址或者编译器(不正确的)坚持要看到定义式,就必须提供一下定义:

const int GamePlayer::NumTurns;//记得吧式子放入头文件而不是实现文件

由于在声明时获得初值,因此定义时不可以再设初值

PS:无法用#define创建一个class专属常量,因为#define并不重视作用域。一旦宏被定义,它在其后的编译过程中有效(除非被#undef).

所以#define不能定义class专属常量,也不能提供封装性。

旧编译器也许不支持static成员在声明式上获得初值。此外“in-class初值设定”也只允许对整数常量进行。那就把初值放在定义式。

但如果class编译期间需要 一个class常量值,如上述的Gameplayer::score即编译器坚持要在编译期间知道数组大小。那么可以改用”the enum hack“补偿做法。

the enum hack补偿做法:理论基础:一个属于枚举类型的数值可权充ints被使用。

例如:

class Gameplayer{
    private:
	enum{NumTurns=5};//"the enum hack"令NumTurns成为5的一个记号名称
    	int scores[NumTurns];//ok
}

从这里来看enum hack在行为方面类似于#define而不是const。

enum可以帮助你阻止别人获得一个pointer或reference指向你的某个整数常量

define误用,以它实现宏,宏看起来像函数,但不会招致函数调用带来额外开销。

错误示范:宏夹带宏实参,调用函数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<typename T>
inline void callWithMax(const T& a,const T&b){
    f(a>b?a:b);
}

callWithMAX是一个真正的函数,他遵守作用域和访问规则

  • 对于单纯常量,最好用const对象或者enums替换#define
  • 对于形似函数的宏,最好改用inline函数替换#defines
posted @ 2022-04-01 14:40  BailanZ  阅读(26)  评论(0编辑  收藏  举报