zoukankan      html  css  js  c++  java
  • 【C++】#define vs const 、enum & inline

    参考书籍:

    《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未经允许请勿用于商业用途

  • 相关阅读:
    robotframework之配置文件和资源文件
    robotframework-ride创建测试用例过程
    Robot Framework的环境搭建
    Robot Framework的初始化与清除
    robotframework的安装1
    《这么慢,那么美》摘录
    小白摸索记(一)
    学习笔记-express路径问题
    学习笔记-使用cmd命令行创建nodejs项目
    学习笔记
  • 原文地址:https://www.cnblogs.com/dancingrain/p/3405205.html
Copyright © 2011-2022 走看看