《Effective C++》《让自己习惯C++——2、尽量以const、enum、inline替换#define》

发布时间:2024年01月17日

term2:Prefer const,enums,inlines to #defines

这个条款的本质:在编程过程中多用编译器少用预处理器。因为#define是宏定义,他不被视作语言的一部分。而const,enums,inlines对应着3种不同的情况。

(1)const:常量替换

举个栗子:

#define PI 3.14159

这段代码在程序预处理过程中就执行了,这个记号名称PI,可能没有进入记号表,也从未被编译器看见。这样的话有一个不好的后果和一个潜在的风险。不好的后果就是如果程序编译报错,因为记号表没有收录这个记号名称,他会抱3.14159出错,会给后期的排查带来很大的困难;潜在的风险就是,他会直接替换,而不会对类型进行检查。
针对记号名称没有出现在记号表有一个解决办法:

//以一个常量替换上述的宏
const double PI = 3.14159;

除了以上的常量替换,有2种情况相对来说特殊一些:
(1)定义常量指针
简而言之,有必要将指针申明为const;
(2)class专属常量
class相较于struct更加强调作用域,#defines不能满足这个要求,而且不能提供任何封装性,所以对于有些专属常量,谨慎使用#defines。

class GamePlayer{
private:
		static const int NumTurns = 5; //声明式
		int scores[NumTurns];
		//to do sth;
		//高级编译器支持
}

如果程序中需要对class内的专属常量进行取地址的操作,需要另外提供定义式

const int GamePlayer::NumTurns; //定义式

旧式的编译器不支持在变量声明时获得初始值,所以按照一贯的习惯(变量在头文件声明,在实现文件中定义)。

class CostEstimate {
	static const double PI;			//常量声明
	//to do sth;					//位于头文件

};
const double
	CostEstimate::PI = 3.14159;      //常量定义
									 //位于实现文件内

但是如果遇到上面的例子,你要声明一个数组,编译器坚持要知道数组的大小。这时候就可以使用“the enum hack"补偿做法。

(2)enums :枚举量替换

一个属于枚举类型的数值,可以权充ints使用。

class GamePlayer{
private:
	enum{NumTurns = 5};
	int scores[NumTurns];
	...
}

(3)inlines : 内置函数替换

另一种情况就是实现宏,使用宏的时候,尤其要注意,表达式外要加上足够多的括号,不然,会有一些意想不到的错误。
举个栗子:

//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次

++的次数取决与和谁比较,这样的宏定义,就有很多隐藏的风险。因为宏不会像函数调用那样产生额外的开销,但与此同时会有不可预料的行为以及类型不安全的情况发生。
C++给出的解决方法是:templete inline函数

templete<typename T>
inline void callwithMax(const T&a,const T&b){
	f(a > b ? a: b);
}

但是#define在现阶段是无法被代替的,要充分了解他,并知悉他潜在的风险。

总结

书山有路勤为径,学海无涯苦作舟。

参考

《Effective C++》

文章来源:https://blog.csdn.net/zwh1298454060/article/details/135643927
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。