EFFECTIVE C++ 条款02 尽量以const, enum, inline替换 #define
1.为何要替换#define
-
问题就在于 #define(宏) 并不被视为语言的一部分
-
譬如
#define PI 3.14
- 由于预处理进程对宏 PI 进行了处理,在编译器接手代码时,也许根本就不认识PI。当你使用这个这个常量获得了一个编译错误信息时,可能这个错误信息会提到3.14而非PI。这会消耗不必要的时间去排查错误。
-
又如
#define CALL_WITH_MAX(a,b) f((a)>(b)?(a):(b))
-
这个宏的意思是以a和b的最大值调用函数f
-
然而,你会发现一些不可思议甚至疯狂的事情,比如
int a=1,b=0; void f(int a){ std::cout<<a<<std::endl; } CALL_WITH_MAX(++a,b);//a累加2次 CALL_WITH_MAX(++a,b+10);//a累加1次
这里在f调用之前,a的累加次数竟然和它与谁进行比较有关。
-
2.如何解决宏的问题
1.使用常量const或枚举enum替换宏
-
比如
#define PI 3.14
完全可以改成const double Pi=3.14
,,这样就可以解决编译器的报错不明问题 -
在这里,有两种特殊情况值得考虑
-
定义常量指针
为方便不同源文件包含,常量定义式通常会放在头文件中,因此有必要将指针声明为const。譬如:
const char* const Name="zj"
-
类专属常量
-
当你想要将常量的作用域限制于class内,你必须让它成为class的一个成员。而为了保证常量至多有一个实体,必须让其成为static成员:
class GamePlayer{ private: static const int NumTurns=5; int scores[NumTurns]; };
-
实际上,这里的NumTurns是一个声明式而非定义式。通常情况下,C++要求你对你所用的任何东西提供一个定义式。但是如果它是一个class的专属常量又是static且为整数类型(如int,char,bool),则需要特殊处理。只要不取其地址,你就可以声明并使用而无需提供定义式。但是如果你需要对其取地址,或者你的编译器(抽风)坚持要定义式(劝换编译器),则需要提供定义式:
const int GamePlayer::NumTurns;//在声明时已经赋值所以这里不需要重复赋值
//这个式子应该出现在实现文件而非头文件
-
然而,有些编译器(错误地)不允许static整数型常量完成“in class 初值设定”,可以使用“the enum hack”补偿做法。这种做法的理论基础就是“一个属于枚举类型的数值可权充ints被使用”
class GamePlayer{ private: enum{ NumTurns=5 }; int scores[NumTurns]; };
“enum hack”行为某方面说比较像#define而不像const。譬如,取一个const的地址是合法的,但是取一个enum的地址就不合法,而取#define的地址通常也是不合法的。
-
-
2. 用template inline函数替换含参数的宏
-
使用template inline函数可以获得宏的效率以及一般函数的所有可预料行为和类型安全性。
template<typename T> inline void callWithMax(const T& a,const T& b){ f(a>b?a:b); }
3. 总结
1.使用consts,enums,inlines可以降低我们对预处理器的需求。
2.谨记:
- 对于单纯常量,最好以const对象或enums替换#define
- 对于形似函数的宏,最好改用inline函数替换#define