这里要介绍分析的是effective c++中的条款02:尽量以const、enum、inline来替换#define
在写之前还是要先声明,本文只是浅谈浅析,必要的地方也只是潜译,本文可以当做是一个简单的总结,对于有些不愿意看英文原文或者觉得书籍太过冗长的朋友来说可以直接参阅我的总结,技术帝学术帝勿喷 ,谢谢。
#define是我们在C++中编写代码常用的手段,指定宏有时候可以很好的方便我们,那么#define到底有哪些好处呢?
1、#define从某种程度上来说可以防止数据被修改;
2、#define不会从内存中分配空间,同样也无法被获取地址和引用;
3、#define宏函数不会造成额外的内存开销,切执行效率好;
我们知道#define预编译的内容会在编译器一开始处理源码的时候就被替换移走了,所以#define的内容还是会在编译上造成很多不可忽视的错误,下面我们从几个方面来替换#define的优势并尽量减少其风险发生。
一、使用const来替换#define
使用const来替换#define可以更优质的实现数据不被修改,同时还额外的增加了访问控制及作用域的好处。
当我们预定义一个宏时:#define RADIO 1.23
我们可以改变其为const常量的形式如:const double radio = 1.23;
这样有什么好处呢?第一,radio会以一个常量的形式进入记号表;第二、不会产生不必要的盲目替换;第三、数据不会被修改;
当const继承了#define的优点时,还能产生额外的好处:
const常量可以作为类的专属常量,我们可以把const常量声明成类的成员变量,如果还想保证此常量最多只有一份实体,只实例化一次,必须让其成为static静态成员;
class A
{
private:
static const int value = 1;
int array[value];
...
};
同时value常量还被限定了访问修饰符,限定了作用域;当然了,静态数据成员在初始化的时候可以在实现文件中,而头文件只提供声明:
class A
{
private:
static const int value;
...
};
const int A::value = 1;
二、使用enum来替换#define
有时候会出现这种情况,我们在类中声明一个数组,数组在编译的时候必须知道数组大小value,可是编译器不许我们在类中初始化value的值,即类中只能声明,这时候该如何是好?
这时候为了不使用#define,可以使用enum,根据原则:一个属于枚举类型的数值可以权充int被使用。
如:
class A
{
private:
enum{value = 5};
int array[value];
..
};
enum在这里被称为枚举堆enum hack:不允许被取地址。const的地址可以被获取,而#define和enum的变量地址则不能,如果不想让别人获取指针或引用,可以使用enum。enum和#define不会导致非必要的内存分配,const则有可能因编译器的需要而额外分配内存。
三、使用inline替换#define
我们知道#define可以用来定义函数宏:#define CALL_WITH_MAX(a,b) f((a)>(b)?(a):(b))
这样定义的函数宏十分复杂,我们必须为每个参数加括号()。并且在使用的时候也会产生许多不可思议的错误。可是宏函数在使用上效率高,不会产生额外的内存开销。
为此我们可以使用inline函数来获得很宏函数一样的效率,并且获得访问修饰符与作用域两个优势。
template <typename T>
inline void callWithMax(const T&a,const T& b)
{
f(a>b?a:b);
//函数的参数可以使用判别式;
};
总结:有了const、enum和inline,我们对预处理器#define的需求降低了,const可以定义专属常量和作用域,enum可以不被取地址,不会导致内存的额外分配,inline同样可以降低函数调用的开销,获得与#define一样的效率,且存在作用域与访问规则。但是#define仍然是必须的,#ifdef.....#ifndef等可以很好的控制编译。