条款02:尽量以const,enum,inline替换#define
宁以编译器替换预处理器。
1、const
#define ASPECT_RATIO 1.653
在预处理的时候,会使用实际值替换宏名,程序中所有的ASPECT_RATIO 替换为1.653.
会出现的问题:这个宏的记号名称在预处理的时候就被替换为数字,不会进入编译器阶段的符号表,出错的时候,是提示一串数字。
解决方法:使用一个常量const来替换上述的宏:
const double ASPECTRatio = 1.653
注意:使用宏可以定义数组的大小,编译不会报错,使用const也可以,int a[ASPECTRatio].
有两种特殊情况需要注意!
1)定义常量指针。
const char* const authorName = "Scott Meyers";
*之前的是底层const,说明指向的数组是常量,数据不能更改,*之后的是顶层const,说明指针是个const,指针的指向不能更改,但是数据可以。
2)class专属常量。
为了将常量的作用域限制在class内,它必须称为class的一个成员,而为了确保此常量至多只有一份实体,必须使它称为一个static成员:
class GamePlayer{ private: static const int NumTurns = 5;//常量声明式 int scores[NumTurns]; ... }; //下面的定义式放在cpp文件而不是头文件里面 const int GamePlayer::NumTurns;//定义式
由于class常量已经在声明的时候获得初值,因此定义时不可以再设初值。
扩展:
//声明和定义的区分: //extern是声明,在类内的变量是声明,有函数体的是定义,其他都是定义。变量和对象不加extern永远是定义,类中除外
注意:#define的定义域只有遇到#endef才结束,所以无法定义一个class专属常量。
老的编译器不支持static成员在声明的时候赋予初值,那么只有在定义时赋初值。还可以使用枚举类型进行赋值操作。
2、enum
枚举类型补充知识:
枚举类型使我们可以将一组整型常量组织在一起。和类一样,每个枚举类型定义了一种新的类型。枚举属于字面值常量类型。
枚举类型分为限定作用域的和不限定作用域的。
- 限定作用域的
enum class open_modes{input,output,append}; open_modes nw = open_modes::inout;//需要使用作用域运算符显式的调用枚举成员
默认情况下,枚举值从0开始,一次加1,也可以显式的赋初值,枚举成员是const。
- 不限定作用域的
enum color{red,yellow,green}; enum{red1,yellow1,green1};
名字的作用域就是enum所在的作用域,所以两者的名字不能重复。,可以直接使用{}里面的元素进行赋值操作。
和类一样,枚举也可以定义新的类型,不过必须要使用enum进行赋值。
class CostEstimate{ private: enum{Numturns= 5}; int scores[Numturns]; };
和#define相似的地方:取一个enum的地址是不合法的,而且它不会导致非必要的内存分配。
3、inline
问题的提出:
//以a和b的较大值调用f #define CALL_WITH_MAX(a,b) f((a) ? (b) : (a) : (b)) CALL_WITH_MAX(++a,b);
a的递增次数取决于a和b哪个大,无法预料行为,所以使用template inline函数。
template<typename T> inline void callWithMax(const T& a,const T&b){ f(a > b ? a : b); }
总结:
- 对于单纯变量,最好以const对象或enum替换define;
- 对于形似函数的宏,最好改用inline函数替换define。