条款02:尽量以const,enum,inline替换#define《Effective C++ 改善程序与设计的55个具体做法》

前言

本系列博客将详细学习并介绍《Effective C++ 改善程序与设计的55个具体做法》(第三版)中的各个条款。

本节介绍条款02:尽量以const,enum,inline替换#define!

一、概念

这个条款简单点就是说:尽可能少依赖预处理器,尽量减少#define这种宏定义的使用。

#define可能会导致一些奇奇怪怪的问题(虽然我暂时还没遇到过)。

二、const

省流:用常量代替#define

像以下这种尽可能少用,这种写法可能不会被编译器注意到,这样一来可能ASPECT_RATIO 没进入符号表,那么当出现编译错误时,错误信息可能会提示1.653而不是ASPECT_RATIO ,然后我们还得找1.653是啥,很浪费时间!

#define ASPECT_RATIO 1.653

解决方案:用以下代码去写,用常量去替换,这样的好处一来,这样写肯定会被编译器注意到,二来,#define的写法会让预处理器将宏名称ASPECT_RATIO 替换成1.653会让目标码(object code)多出现一份1.653,而使用从const double的定义方式就不会。注意变量的命名规范和宏不一样!

const double AspectRatio = 1.653;

注意以下两种特殊情况:

1.定义常量指针

先区分一下常量指针(const int * p)和指针常量(int * const p):
常量指针:指向“常量”的指针。常量指针本质上是一个指针,常量表示指针指向的内容,说明该指针指向一个“常量”。在常量指针中,指针指向的内容是不可改变的,指针看起来好像指向了一个常量。指针初始化后可以修改其指向,但是无法修改对象的值。

int a=10,b=20;
const int *p = &a;
p = &b; //允许修改指向
*p = 15; //报错,不允许修改值

指针常量:指针类型的常量。本质上一个常量,指针用来说明常量的类型,表示该常量是一个指针类型的常量。在指针常量中,指针自身的值是一个常量,不可改变,始终指向同一个地址。在定义的同时必须初始化。指针一旦初始化不可再更改,但是其指向对象的值是可变的。

int a=10,b=20;
int* const p = &a;
*p = 15; //允许修改值
cout<<*p;
p = &b; //报错,read-only variable 'p'

回到正题:这里我们在定义常量指针的时候,由于常量定义式通常放在头文件内,所以要将指针(而不是指针指向的东西)置为const,也就是下面这样:

const char* const authorName = "Scott Meyers";
const std::string authoeName("Scott Meyers");  // 最好写成这样

2.class专属常量

另一个注意的点是class专属常量。为了将常量的作用域限制在类内就必须让它成为类中的一个成员,同时为了确保此常量至多只有一份实体,就必须让它成为一个static成员,如下面代码所示:

class GamePlayer{
private:
	static const int NumTurns = 5;  // 此处为常量声明式
	int score[NumTurns];
};

这里有一处不同,正常的static const int NumTurns = 5;这种写法属于定义式,但在这里则是声明式。C++会要求所使用的任何东西提供定义式,而如果是如上情况(它是class专属常量又是static且为整数类型比如int,char,bool)则需要特殊处理。只要我们不取它们的地址,像上面这样写就行了,但如果需要取某个class专属常量的地址,或者某些编译器必须要定义式,那么就需要在一个实现文件(不是头文件)中另外提供定义式,像这样:

const int GamePlayer::NumTurns;

三、enum

接着第二章的尾巴说,如果编译器不允许“static 整数型 class 常量’’完成“in class 处置设定”,那么就可以用枚举代替,像这样:

class GamePlayer{
private:
	enum {NumTurns = 5};  // 此处为常量声明式
	int score[NumTurns];
};

这里能用枚举但不能用#define,因为#define没有作用域的概念,一旦宏被定义,除非遇到#undef,否则它就在其后的编译过程中有效,因此#define无法用来定义class专属常量,使用enum就可以解决,特别是有时候不像取地址,那么对enum和#define去取地址都不合法,enum便能完美取代#define。

四、inline

对于实现函数操作的宏,一般需要给所有实参加上小括号,否则容易出问题,但即使加了,也同样完全避免问题,比如下面的例子:

#define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b))

int a = 5, b = 0;
CALL_WITH_MAX(++a, b);  // a被累加两次
CALL_WITH_MAX(++a, b + 10);  // a被累加一次

调用f之前,a的递增次数取决于“它被拿来和谁比较”,这应该是受了编译过程中有什么不得不做的操作影响,就会带来一些麻烦,此时像这种可能会被频繁调用的小函数就可以用内联函数替代,像下面这样:

template<typename T>
inline void callWithMax(const T& a, const T& b)
{
	f(a > b ? a : b);  // 详见条款20
}

总结

本条款想表达的核心思想就是:当前预处理器虽然还不到退休的时候,但我们要尽可能避免使用它,尽量用const,enum,inline去替代预处理。

日拱一卒,功不唐捐!

  • 17
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值