Effective C++之旅 之 const, enmu, inline and #define

来源:互联网 发布:耐克在淘宝有旗舰店吗 编辑:程序博客网 时间:2024/05/22 11:30

    在开始之前,先简述一下C++语言。以前一直不知道该怎么定义C++语言,因为用C++写个面向过程的程序也一样可以。现在做一个分析,C++语言是一个多重规则的语言,可以理解为一个由多种语言组合在一起的集合。就好比一个瑞士军刀,里面有好多针对不同功能的工具。

    C++基本由四种语言组成:c,OOC++,Template C++和STL。

C就是我们所说的纯C语言,没有模板,没有重载,没有异常;

OOC++就是典型的面向对象编程,所以包括三个基本特征:encapsulation, inheritance and polymorphism。

template C++即泛型编程,威力强大。

STL,它是一个template程序库,功能强大。

不同的语言,有不同的规则,选择对应语言,遵循对应的规则,就能编出高效的程序,比如C语言的内置类型int,pass by value的效率高于pass by reference,而OOC++和template C++的对象,由于构造函数和析构函数的存在,pass by reference比pass by value要高效。同理,STL,它的iterator和函数对象都是基于C指针塑造出来的,所以pass by value更高效,你在STL里找不到reference的应用。

 

    关于C++的分析就这么多,下面继续标题上的point

POINT 3,尽可能的用const, enum替换#define定义的字串或数值常量

    首先说用#define定义常量字串,数值存在的问题,比如#deine PI  3.1415926

    1. 编译器不知道PI的存在,因为它已经被预处理器改为3.1415926,所以不会将PI加入到symbol table中,编译的时候,我们根本不能靠PI来搜索到它,出问题时很难定位。

    2. 没有类型的概念,做数值可以,做字串用也可能,编译器发现不了类型不匹配的错误。

    3. 没有常量scope的概念,所以很容易成为一个全局的变量。没有封装变量而言。

    下面要说的const就能解决所有上面的问题了,所以用const替代#define定义的字串,数值常量是完全可以的。

const的使用,可能大家也都知道,这个以后会详细说。这里只说一下用const实现的scope变量的限制。以一个class的定义为例:

    class A

    {

         static const int numbers = 5;   //这种叫in class 初值设定

         int scope[numbers];

         ...;

     }

     这里你看到的numbers是声明,而不是定义,通常C++要求我们必须提供定义后才能使用,但是对于statc且为整数类型的变量(int, char, bool等),只要不取它们的地址,我们就可以无需定义直接使用,所以上面的scope数组的numbers可以直接使用。

     但是如果编译器不支持这种的话,那我们就不得不写出定义式了,其实也很简单,只是在class 定义外面做如下定义:

     const int A::numbers;  //这里无需赋值,因为numbers在声明时已获得初值,所以这里不用再赋值。

如果编译器不支持声明in class初值设定,那拿到外面赋值也是一样的,const int A::numbers = 5;  但是这样的话,scope数组里的numbers就不能使用了, int scope[numbers]; 就不能这样定义了,所以这里有另外一种所谓的enum hack的方法,如下:

     class A

     {

           emum {numbers = 5};

           int scope[numbers];       //这就没有问题了

           ...

      }

显然,这里的enum起到了一个#define的作用,但是它是由scope限定的,比define更具有安全性和私密性。

这里需要提到一点,通常const 定义的变量,我们是可以访问该变量的地址的,但是enmu和define的定义的变量,我们是访问不了地址的。

      总结:常量定义的优先顺序为:const > enum > define,原则就是:能让编译器处理的就不要用预编译器帮忙。

 

POINT4, 用inline替代#define定义的宏函数

      对于另外一种常见的#define使用的方式就是定义宏,如下:

      #define MAX_VALUE(a, b)    fun( (a) > (b) ? (a) : (b) )

这是个典型的函数宏,这样做的好处是,它可以直接把后面的语句替换到code中,而不会导致function call带来的而外开销,如果你这么做当然就会有这个开销:

      void MAX_VALUE(a, b) { func(a > b ? a : b); }   //这里有个调用的开销

用define来实现这样的宏有如下的风险:

      1. 实参的封闭性,a 必须用小括号包起来,原因大家应该都知道,因为a可能是个表达式。

      2. 逻辑混乱,可能出现以下的问题:

      int a = 5, b =0;

      MAX_VALUE(++a, b);    //这里,a会被加两次,最后 a = 7被传给fun

      MAX_VALUE(++a, b+10);   //这里a被加一次,最后b = 10被传给fun

a被加几次竟然取决于b,逻辑很乱。所以这里我们提出用inline来代替#define,完全可以摆脱这种问题,如下:

      template <typename T>

      inline void max_value(const T& a, const T& b) { fun( a > b ? a : b); }

同时,因为定义为内联函数,所以编译器当然会把解析过的code直接替换max_value,同样也没有function call的开销,何乐而不为呢?

 

      本节总结:

      对于单纯的变量,int,char,string等,尽量用const,enum替代#define

      对于函数宏,用inline替代#define

 

待续... ...