天天看点

条款 02:尽量以 const, enum, inline 替换 #define

文章目录

    • 一、用const替换#define
      • 1. 发生编译错误时,很难追踪
      • 2.没有封装性,不能限制作用域
    • 二、用enum代替#define
    • 三、用inline代替#define
    • 四、请记住

一、用const替换#define

  #define 存在以下的问题:

1. 发生编译错误时,很难追踪

  如果运用了上面定义的常量时,由于在预编译时,所以用到了PI的地方全部都已经被替换掉了,若发生编译错误,错误信息只会提到3.14,而不是PI,非常的难以追踪。

  解决方法就是用常量替换#define

const double PI = 3.14;			//则替换了#define
	const double* const PI = 3.14;	//若是使用指针需要const两次
	*PI = 3.1415926;				//这就会报错,原因条款三说明
	double i = 2.12;
	PI = &i;						//这也会报错
           

2.没有封装性,不能限制作用域

  若想创建class专属常量,无法用#define创建,因为它的作用域并没有限制,一旦宏被定义,就一直有效(除非去#undef),因此使用const常量,只要将其作用域限制于class内(即让它成为class的一个成员),而为了让它只有一份实体,让它成为一个static成员。

class GamePlayer
{
	private:
		static const int NumTurns = 5;	//静态常量声明式,所有实例共享
		static const int Num;			//静态常量声明式
		int scores[NumTurns];			//使用上面定义的常量
}
           
#include "gameplayer.h"

	const int GamePlayer::Num = 5;			//Num定义式,在实现文件中
           

  上面的常量NumTurns是声明式而非定义式,而对于一般的staic变量(Num),是在头文件中声明它是static,而在源文件中定义的;而对于static const变量,假如这个变量是“整数”类型:int,char,bool,那么直接就可以直接声明,而不用定义。

二、用enum代替#define

  在上面用作数组大小时,用const代替,若不想让别人获得一个指针或印用指向该常量时,enum就可以实现这个约束条件。

class GamePlayer
{
	private:
		enum { NumTurns = 5 };			//enum,令NumTurns成为5的一个标记
		int scores[NumTurns];
}
           

  取enum的地址是不合法的,而取const的地址确是合法的,因此认识enum是有必要的。而这也是template metaprogramming(模板元编程)的基础技术。

三、用inline代替#define

  当用#define实现宏时,宏看起来像函数,但不会招致函数调用带来的额外开销。

#include <iostream>
using namespace std;
#define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b))

int main()
{
	int a = 5, b = 0;
	CALL_WITH_MAX(a++, b);			//a累加两次
	CALL_WITH_MAX(a++, b++);		//a累加一次
	return 0;
}
           

  在上面的main函数中,宏中的两个实参较大值调用函数 f (),调用宏时,a的递增次数竟然取决于a和b的大小;并且类型是否相同,也没有检查。

  而使用template inline函数,则可以解决上述问题。

template<typename T>					
inline void callWithMax(const T& a, const T& b){
	f(a > b ? a : b);				//因为不确定a, b的类型,用模板编程
}
           

四、请记住

  • 对于单纯常量,最好以const对象或enums替换#defines。
  • 对于形似函数的宏,最好改用inline函数替换#defines。

继续阅读