浅谈了内联函数,似乎也该对宏重新认识下了,然后再总结下宏和内联函数的区别。毫无疑问,平时使用宏要比使用内联函数多得多,但是自己却没有去总结过这个再也熟悉不过的宏。很多时候,我们使用一样东西,往往因为它给你带来了方便而一味的使用它,但是却没有去想过它有什么缺陷,什么时候不能用它。下面引用杨凡的论文《浅析C++中内联函数与宏的区别》来说明。

 1.宏的缺陷

     为什么要使用宏呢?因为函数的调用必须要将程序执行的顺序转移到函数所存放在内存中的某个地址,将函数的程序内容执行完后,再返回到转去执行该函数前的地方。这种转移操作要求在转去执行前保存现场并且记忆执行的地址,转回后要恢复现场,并按原来保存的地址继续执行。因此,函数调用要有一定的时间和空间方面的开销,也必将影响其效率。而宏只是在预处理的地方把代码展开,不需要额外的空间和时间开销,所以调用一个宏比调用函数更有效率。我想,这个大家都应该很清楚的了。

      我们常经常定义一些宏,如

      #define CC(x) ((x) > 0 ? (x) : 0)

      就定义了一个宏。

      但是宏也有很多的不尽如人意的地方。宏是用预处理器对宏进行替代,预处理器处在的关键问题使我们可能认为预处理器的行为和编译器的行为一样。当然,有意使宏在外观上和行为上与函数调用一样,因此容易被混淆。但是当微妙的差异出现时,问题就会出现了。

      举个简单的例子: #define f(x) (x+1)

      现在假如有一个像下面的f调用了f(1),预处理器展开它,出现下面不希望的情况: (x) (x+1) (1) 。出现这个问题是因为在宏定义中f和括号之间存在空格缝隙。实际上调用宏时可以有空格空隙。像下面的调用:f(1)依然可以正确地展开为(1+1)。

      上面的例子虽然微不足道,但是问题非常明确。

       宏的定义容易产生二义性。

       在宏调用中使用表达式作为参数时,问题就出现了。第一个问题是表达式在宏内展开,所以它们的优先级不同于我们所期望的优先级。for example:

      #define DD(x)  (x*x)

      我们用一个数字去调用它,DD(10),这样看上去没有错误,结果返回100,是正确的,但是我们用DD(10+10)去调用的时候,我们期望的是400,但是结果 10+10*10+10 = 120,这显然不是我们想要的结果。

      避免这种错误的方法是,就是给宏的参数加上括号: #define DD(x)  ((x)*(x))

      这样可以确保不会出错,但是即便这样,这个宏仍然有出错的可能性。这就是第二个问题,这个问题更加微妙。不像普通函数,每次在宏中使用一个函数,都对这个参数求值。只要宏仅用普通变量调用,这个求值就开始了。但假如参数求值有副作用,那么结果可能出乎预料,并肯定不能模仿函数行为。

      例如使用DD(a++)调用它,他们本意是希望得到(a+1)*(a+1)的结果,而实际上呢?此时宏的展开是:(a++)*(a++),如果a的值是4,我们得到的结果是5*6=30。而我们期望的结果是5*5=25。事实上,在一些C的库函数中也有这些问题。例如:Toupper(*pChar++)就会对pChar执行两次++操作,因为Toupper实际上也是一个宏。

      此外宏不能访问对象的私有成员。

      很清楚,这不是我们想从看起来像函数调用的宏中所希望的。在这种情况下,明显有效的解决方案是设计真正的函数。

2内联函数与宏的区别

      我们可以看到宏有一些难以避免的问题,怎么解决呢?答案是内联函数可以解决这些问题,我们使用内联函数来取代宏的定义。

      内联函数和宏的区别在与,(1)宏使用预处理器对宏进行替代,而内联函数是通过编译器控制来实现的。而且内联函数时真正的函数,只是在需要用到的地方,内联函数像宏一样的展开,所以取消了函数的参数压栈,减少了调用的开销。可以像调用函数一样来调用内联函数,而不必担心会产生处理宏的一些问题。

      首先,内联函数可以完全避免宏易出现的二义性。

  for example:inline int DD(int x) {return x*x} 

  int y = DD(a++);

     编译器会产生如下代码:

     int y;

     a++;

     a*a;

      另外,(2)内联函数可以访问对象的私有成员函数,内联函数在C++里面应用最广的,应该是用来定义存取函数。我们定义的类中一般会把数据成员定义成私有的或者保护的,这样,外界就不能直接读取我们类成员的数据了。对于私有或者保护成员的读写就必需使用成员接口函数来进行。如果我们把这些读写成员函数定义为内联函数的话,将会获得比较好的效率。

      当然,内联函数也有一定的局限性,就是当函数的执行代码太多的时候,如果内联函数的函数体过大,一般的编译器会忽略内联方式,而采用普通的方式调用。这样内联函数就和普通函数执行的效率一样了。

    

     

     

     

    

posted on 2011-10-18 11:30  陈文斌_5months  阅读(2107)  评论(0编辑  收藏  举报