其实我一直比较喜欢C语言,对C++的了解少之又少,但是最近由于需要翻看起C++的书籍来。在C++沉思录当中,看到了这样一句话:“我还指出,由于这些成员函数定义在Trace类自身的定义内,C++会内联扩展它们,所以就使得即使在不进行跟踪的情况下,在程序中保留Trace对象也不必付出许多代价。——Koenig,《C++沉思录中文版》,page2”。K先生所举例的代码如下所示:
1 class 2 Trace{ 3 public : 4 Trace(){noisy = 0;} 5 void print(char *s){if(noisy) printf("%s,"s);} 6 void on(){noisy = 1;} 7 void off(){noisy = 0;} 8 9 private: 10 noisy = 1; 11 }
其实这段代码风格并不是所提倡的(函数的声明和定义其实应该分隔开的),当然K先生这里的目的是举个例子说明问题。
但是这里确实引起了我的疑惑,为什么K先生说“即使在不跟踪的情况下,在程序中保留Trace对象也不必付出许多代价”?按理说,这种把函数定义直接写在类内部的方式直接导致了函数的内联,而内联确实膨胀程序,因此我产生了疑惑,这里先埋下伏笔,在知识梳理完毕后尝试解惑故。
下面的知识引用自CSDN博客,http://blog.csdn.net/lidh04/article/details/3795802和http://blog.csdn.net/zhangchao3322218/article/details/8099747。
首先在C语言中也是存在inline的,这里的说明是针对gcc 编译器的。在gcc 中,有三种方式的内联,分别为static inline,inline,extern inline,下面分开介绍:
1.inline。定义的说法:这是一个针对函数定义的关键字(注意并非针对声明),用于对编译器的建议,当然编译器有理由拒绝,其作用为不专门为其在调用处产生汇编代码,只是像宏那样进行展开。
注意,以下情况不在上述讨论的范围内:
一、回调
二、递归
例外情况的原因请读者自行思考。而且,被这样生命的函数可以在文件外被调用,像这样
extern func();
但是需要注意的是,不可以出现这种情况:
extern inline function();//这是不对的
这是我们下面讨论的extern inline,使用情况很狭窄,请注意。
2.static inline 。可以理解为static和inline这两种属性的叠加。故而,相对于inline来讲,static限制了这种函数只能在被定义的文件中使用,不可以被extern。
3.extern inline。这个不能想当然的理解为extern 和 inline 属性的叠加。实际上,gcc的extern inline 函数只能被内联,并不会产生出独立的汇编代码,即使出现了上面提到的两种意外;而且,extern inline 允许在同一个文件中出现重名函数。
但是我们一般没有理由使用这样一种东西,其存在的意义我现在还没有搞明白。
下面来看一下在C++当中的inline。
其实这两种语言的的inline很相似的,C++中比C中多的那一部分应该使我们在开篇提到的Class的内部会自动将已经定义好的函数内联,但是这个并不是值得提倡的。在编译的时候,inline函数省去了函数调用的开销(esp,ebp,寄存器数据改变,局部性改变,上下文切换等等),因此规模较小的inline函数会增加性能。
那么我们回到刚开始的疑惑,尝试着解答。
回顾一下,我的疑惑是K先生的“在程序中保留Trace对象也不必付出许多代价”,产生疑惑的原因是我对于内联的理解:在调用处进行函数的展开,导致程序的膨胀。
但是由于这里被内联的函数很小,根本和膨胀谈不上关系,在我们不回收Trace对象的时候,也不会产生太大的浪费,也就是K先生所言“代价”。
c++和c语言的inline关键字对比