条款02:用const,enum,inline
而不是#define
推荐使用const
定义常量而不是宏,值得注意的是,类内部定义时静态常量时,推荐使用类外的定义式和初始化。
类内的初始化可能不能被识别,视编译器而定
// 声明文件
class B {
private:
// 这里的叫做声明式
static const int AIntVal;
// 对某些编译器而言,支持整数常量的类内初始化,比如:
// static const int AIntVal = 2;
// 请注意,也只有整数类型才可以,下边的double不可以
static const double ADoubleVal;
};
// 实现文件
// 下面的则是定义式
const int B::AIntVal = 2;
// 如果之前有类内初始化,那么类外也可以只写一个定义式:
// const int B::AIntVal;
// 如果不需要对这个变量进行取地址操作,定义式也可以不写
// 但是这也要视编译器的实现而定,有些编译器会强制要求提供一个定义式
const double B::ADoubleVal = 2.5;
一个类内初始化的必要应用场景是在类的编译期间需要使用这个常量的值,比如:
class B {
static const int NameLen = 2;
char name[NameLen];
};
此时NameLen
变量便不可以在类外初始化了,而如果编译器不支持类内初始化,此时我们可以使用enum
关键字。
class B {
enum { NameLen = 5 };
char name[NameLen];
};
这样声明的NameLen
和#define
得到的常量的行为是十分相似的,比如不能使用取地址等操作。
最后是使用inline
关键字,宏函数太过丑陋,因此使用inline
搭配template
是更好的做法。
#define CALL_WITH_MAX(a, b) \
(f((a) > (b) ? (a) : (b)))
// 丑陋的括号,而且哪怕是这样还是会出现问题,比如有人会用
int a = 2;
CALL_WITH_MAX(a++, 0);
a = 2;
CALL_WITH_MAX(a++, 4);
// 这两次调用后a的值是不一样的
// 所以最好使用
template<typename T>
inline void CALL_WITH_MAX(const T& a, const T& b) {
f(a > b ? a : b);
}
而且inline
函数还会提供一个函数的作用域,而define
里如果添加了新的变量,会造成作用域的混乱。当然也是有解决办法的:
#define TEST_FUNC(a) \
do { \
int inner = 0; \
} while(0);