条款2:尽量以const、enum、inline替换 #define
或者可以这样说,尽量用编译器处理预处理器。
#define ASPECT_RATIO 1.653
记号名称ASPECT_RATIO也许从未被编译器看见,也许在编译器开始处理源码之前它就被预处理器移走了,因此记号名称ASPECT_RATIO有可能没有进入记号表,当用此常量得到一个编译错误时,会感到困惑。
解决方法是以一个常量替换上述的宏(#define)
const double AspectRation=1.65;
作为一个语言常量,AspectRation肯定会被编译器看到,一定会进入记号表内。
这时有两种特殊情况:
1、定义常量指针时
由于常量定义式通常被放在头文件内,因此有必要将常量指针(不只是其指向的对象)声明为const,如:const std::string authorName("Scott Meyers")
2、class 专属常量
为了将常量的作用域限制于class内,必须让它成为class的一个成员,而为确保此常量至多只有一份实体,必须让它成为一个static成员
class GamePlayer { private: static const int NumTurns=5;//常量声明式 int scores[NumTurns]; ......... };
只要不取专属常量的地址,声明之后便可以使用他们而无需提供定义式。
但如果需要取class专属常量的地址,则必须另外在实现文件中而非头文件中提供定义式如下:
const int GamePlayer::NumTurns;//NumTurn的定义 //由于声明时已经赋初值,所以定义时不可以再设初值
注意:我们无法用#define创建一个class专属常量,因为一旦宏被定义,它就在其后的编译过程中有效,并不重视作用域。
但是有的编译器不支持声明赋初值,那么你可以将赋初值放在定义式内
如果你的编译器不允许class内的static整数型常量完成初值设定,可改用枚举常量enum
理论基础是:一个属于枚举类型的数值可以充当整型使用。如下:
class GamePlayer
{
private:
enum{NumTurns=5};//令NumTurns成为5的一个记号名称
int scores[NumTurns];
.....
};
另一个常用的#define的误用情况,是用它实现形似函数的宏,这时你必须为每个实参添加小括号,但即使这样,有时也会出现错误
//以a和b的较大值调用f
#define CALL_WITH_MAX(a,b) f((a)>(b)?(a)>(b))
为了避免这种错误,我们可以用inline代替#define
template<typename T>
inline void callWithMax(const T&a,const T&b)
{
f(a>b?a:b);
}
总结:对于单纯常量,最好以const对象 或enums替换#defines
对于形似函数的宏,最好改用inline函数替换#defines
条款3 尽可能使用const
const修饰指针,如果关键字const 出现在星号左边,则表示被指物是常量,如果出现在星号右边,表示指针自身是常量
const修饰迭代器:
std::vector<int>vec;
.....
const std::vector<int>::iterator iter=vec.begin();//iter的作用像个T*const
*iter=10;//没问题,改变iter所指的值
++iter;//错误!因为iter是const
std::vector<int>::const_iterator citer=vec.begin();//citer的作用像个const T*
*citer=10;//错误!citer指向的值是const
++citer;//没问题
const 用于函数声明:令函数返回一个常量值
const用于成员函数: