现在的位置: 首页 > 综合 > 正文

【C++】#define vs const 、enum & inline

2018年02月01日 ⁄ 综合 ⁄ 共 2471字 ⁄ 字号 评论关闭

参考书籍:

《Effective C++ 》

《C++ Primer》

《C++ 编程思想》上下册


算来已经好久没有写博客了,周末抽点时间记录下最近学习的一个小问题吧。

一直在使用C++写个小实验,以前没有写过特别大的工程,也很少会考虑到使用和不使用#define这个宏的好处和有点,最多也就是知道它是在预处理时期“盲目替换”代码的,因此不会牵涉到函数调用也就更不会有函数调用带来的堆栈开销。今天稍微详细了解了一下,所以几记录下来:

首先,简单罗列一下#define的优点和好处:#define不分配内存,它是预编译命令,编译之前进行宏替换;使用带参数的#define可以完成类似于函数调用的功能,而不用付出函数调用的代价(函数调用前需要保留函数的现场,函数返回时需要恢复现场等)。不过,我这里要说的重点不是#define带来的有点,而是它给我们带来的麻烦以及我们应该怎么样去解决。

  • #define没有类型,不能够进行类型检查;
  • #define的宏不进入记号表(symbol table),因此程序出错时很难发现;
  • #define只是盲目地替代,因此会出现不可预知的错误,且不易被发现;
  • #define没有作用域(scope)的概念,一旦定于就会在随后的编译过程中有效;
原来#define有这么多的缺点和副作用,那么在我们的程序中就应该尽量避免使用#define,当然了凡事都不能够因噎废食,在有些场合它还是不可替代的(如#fidef/#define/#endif)。下面来分析一下如何尽量少的使用#define:

#define 没有类型,不能进行类型检查,也不进入记号表,不会被编译器看到(只是被预处理器看到罢了)。
假设你在程序中有这么一行

#define PI 3.14159

当你运用此常量的到一个编译错误的时候,有写编译器可能会提示3.14159这个数字,而不是PI本身,所以就会让你感到莫名奇妙。甚至有时候这个宏是由别人在其他文件中定义的,那么你就可能话费更多的时间去追踪这个错误。
为了解决这个问题,可以使用一个常量来代替上面个的宏

const double Pi = 3.14159

这样,编译器就能够看到,当然也会进入符号表。在编译过程总出现错误的话编译器就会提示Pi变量出错,而不再是3.14159这个magic number,你也就不用花费更多的精力去追踪错误的罪魁祸首是谁了。当然了,编译器也能够进行数据类型的检查,因为Pi有自己的数据类型。

另外,使用const代替#define的另一个好处就是能够限制变量的作用范围,例如可以定义class专属的常量
class A
{
public :
static const double Pi ; 
};

当然了,这里只是声明了一个作用于在A中的静态常量,还需要对它进行定义

const double A :: Pi = 3.14159 ;

这样,就建立了一个class A专属的常量,这一点#define是做不到的。同样,#define更不能够进行封装,也就是说,Pi可以被定义为A的私有常量,也可以定义为其protected常量,而#define是不能的。

到此,似乎所有的问题都已经解决了,可是仔细想想还是有写遗漏的——加入我想限制对常量进行去地址和reference操作怎么办???#define能做到,你不能对一个宏进行取地址,也不能建立一个对它的reference,但是const变量是允许的!!!难道我们应该重新使用#define吗??我想enum会给我们一些思路吧 ...... 

加入我们写成下面代码的形式:

class A
{
public :
enum { Num = 100 };
};

OK , 你不能取Num的地址或者reference了吧!!不过还是有问题的 .... 那就是这仅仅适用于整形数的情况 ??? 两外的情况该怎么办啊 ?? 这个我想以后在说吧(后面的博客会详细说明)。


好了,说了这么多,可能有人已经有疑问了,你一直都是再说#define对于定于变量时候的缺点和替代办法,那么当带有参数的时候该怎么办啊 ????  
这个是最让人头疼的事情了,所以我放到了最后说 。。。。 

先看一段代码吧:

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


int main (int argc, char **argv)
{
	int n1 = 3, n2 = 2;
	int result =  MAX(++ n1, n2);
	cout << result << endl;
	return 0;
}

我们的本意是得到++n1 ( = 4)和 n2(= 2)中的较大的至,显然这个值应该是4,可你觉得我们会得到正确的结果吗???先问问VS吧  ...... 


哦哦哦哦哦 ???? 怎么是5啊???
对,没有错,就是5!!!!并不是我们所需要的结果,我们可以展开一下,就会发现其实++n1在程序中被执行了两次,所以得到的是5而不是我们期望的4!
同时,你也会发现,我们在写MAX的宏定义的时候大各个参数都加了小括号,这样做是必须的,当时会让我们眼花缭乱 。。。 那么我们应该怎么样避开呢???——inline是个不错的替代.
#include <iostream>
using namespace std;

template <typename T>
inline T& Max( T& a, T& b)
{
	return a > b ? a : b;
}

int main (int argc, char **argv)
{
	int n1 = 3, n2 = 2;
	int result =  Max(++ n1, n2);
	cout << result << endl;
	return 0;
}

这个结果是什么呢?还是先问问VS吧:


OK, 我们要的结果来了!!!
原因就不用赘述了吧 ..... 
inline函数就像正常的函数一样方便,但是却有着像#define一样轻量级的优点!

好了,絮絮叨叨说了这么多,也没有多少有用的东西,权当练手了吧,最后总结一句:尽量减少#define的使用频率,减少追踪莫名奇妙的错误的时间!!

转载请注明作者和出处http://my.csdn.net/doufei_ccst未经允许请勿用于商业用途

抱歉!评论已关闭.