条款2:尽量使用const ,enum,inline替换define
宁可使用编译器而不用预处理器
假设我们使用预处理器:
#define ABC 1.56
这标识符ABC也许编译器没看到,也许它在编译器处理源码前就被预处理器移走了,于是“标识符”ABC没有进入标识符列表(symbol table)中。但是当我们编译程序遇到个错误信息时,可能会带来困惑,因为这个错误信息可能会提到1.56而不是ABC,而下面例子在vs2015上编译的时候,错误信息提示ABC未定义的标识符。假如这个ABC不在我们自己定义的头文件中,我们根本无法知道其来源,追踪不到它。
解决之道就是使用一个常量代替宏定义:
const double abc=1.56;
这样的话abc就是一个标识符,肯定会被编译器看到,必然会进入标识符列表。
1 #include<iostream> 2 using namespace std; 3 4 #define ABC 3 5 const int a = 3; 6 7 int func(const int* n) 8 { 9 return *(n + 1); 10 } 11 12 int main(void) 13 { 14 int x,y; 15 x = func(&ABC);//vs2015会提示表达式必须为左值或者标识符 16 cout << x << endl; 17 18 y = func(&a); 19 cout << y << endl; 20 return 0; 21 }
常量定义特殊情况说明:
(1).由于常量定义被放在头文件中,指针声明为const,假如是char*字符串的话,const就要写两次。
const char* const str="Burgess";
string对象往往写成:
const std::string("Burgess");
(2).class专属常量。为了将作用域限定在class内,必须使它成为一个成员变量。为了保证这个常量只有一个实体,要将它声明为static。
1 class A 2 { 3 private: 4 static const int Num=5;//常量声明式 5 int scores[Num];//使用该常量 6 };
一般情况下,我们还需要在class外进行定义它。
const int A::Num;
由于已经在类里面设了初值,这里不必再初始化。(旧编译器不支持在类里面为static const常量设初值),那么就要:
1 class A 2 { 3 private: 4 static const int Num;//声明式 5 }; 6 const int A::Num=5;//定义式
这个定义式放在实现文件而不是头文件中。
需要说明的是,类里面的函数使用这个变量时,也需要声明为static。
假如上面的数组大小坚持使用一个标识符来代替怎么办呢,因为编译器必须在编译期间知道数组大小,那么我们就可以使用一个枚举类型的数值代替int型数值。
1 class A 2 { 3 private: 4 enum{Num=5}; 5 int scores[Num]; 6 };
这里enum类似#define,不能取地址,只需要获取其值。假如我们不想让别人获取指向某个常量的指针或者引用,可以使用enum。
让我们再返回宏定义。举个误用宏定义的例子。有时我们使用#define实现类似函数的宏定义,但是没有函数调用引来的额外开销。看下面:
1 #define CALL_MAX(a,b) ((a)>(b))?(a):(b) 2 int main(void) 3 { 4 int ret1,ret2,a = 5, b = 0; 5 ret1 = CALL_MAX(++a, b);//现在a=7,不可思议 6 cout<< ret1<<endl; 7 8 ret2 = CALL_MAX(++a, b+10);//a=6 9 cout << ret2 << endl; 10 return 0; 11 }
为什么 CALL_MAX(++a, b+10),此时a=6?
把参数代入表达式就可以知道:
((++a)>(b+10))?(++a):(b+10)
可以看成6>10吗,显然不是。那么就返回b+10=10,而不再执行++a,故a也不会再加1了。
如果不想要这种未知行为的#define,但是还想要宏定义的效率、安全性和可预知性(一切掌握在我们自己手里),那么inline模板函数是个好选择:
1 Template <typename T> 2 inline T const& max1(const &T a,const &T b) 3 { 4 return (a>b) ? a : b; 5 }
这样的话,不会出现上面#define出现的++a加1进行了两次的情况,此时max1是真正的函数。
请记住:
(1).单纯的常量,最好使用const或者enum代替#define;
(2).类似函数的宏,最好以inline函数代替#define。