一、c++ inline函数产生原因
由于函数调用会有一定的时间和空间方面的开销,特别是对于一些函数体代码不大但又被频繁调用的函数来讲,效率是很低的。
在C语言中,可以用宏函数来提高上面那种情况的效率,但宏函数有些缺点,它只是提供一个文本替换的功能,而不是一个真正的函数。所以在C++中引入了inline函数来解决这个问题,它会像宏函数一样在调用函数处用内联函数体的代码进行替换,还遵循函数的类型和作用域规则,所以它能像一般的函数那样进行调用和调试。
二、如何定义inline函数
inline函数只有和函数体定义放在一起是才有效,所以如果像这样声明一个内联函数
- inline void Func();
那么在编译器看来,它与普通函数没有两样,如下所示
- inline void Func()
- {
- // ...
- }
这样它才具有比一般函数更快的执行能力。
还有一种定义方式是在类内部定义的函数。在c++中,如果在类内部定义了函数体的函数,则默认其为内联,而不管是否有inline关键字。所以像如下两个函数Func1、Func2都属于内联。
- class A
- {
- public:
- inline void Fun1(){ //... }
- void Fun2(){ //... }
- }
当然,内联函数并不是万能的,在某些情况下,它不仅不能像期望的那样提升性能,甚至会起反作用。inline只是对编译器的一种提示,而不是命令。也就是说,只要编译器愿意,它就可以随意地忽略掉你的指令,当编译器遇到内联函数时,就会针对函数体的上下文进行优化,以确定是否执行内联。如果编译器认为当前的函数过于复杂、函数体过大,或者这个函数是虚函数,就会拒绝将其内联,这取决与编译器,不同编译器处理方式不一样。
三、在使用inline时,需注意如下几点:
1、在inline函数内不允许用循环语句和开关语句,函数体不能过于复杂;
2、对于内存空间有限的机器而言,慎用内联。过分使用内联会造成函数代码过于膨胀,会占用太多空间;
3、不能对构造或者析构函数进行内联,尽管它们看似很简短。对于这一点看下面这个类Derived的构造函数:
- class Base
- {
- public:
- ...
- private:
- string s1, s2;
- };
- class Derived: public Base
- {
- public:
- Derived(){}
- ...
- private s3, s4, s5;
- };
这个构造函数看起来的确是个内联的好材料,因为它没有代码,实际上,它含有相当多的代码。对于上面这个空的Derived的构造函数,有些编译器会为它产生相当于下面的代码:
- // 一个Derived构造函数的可能的实现
- Derived::Derived()
- {
- // 如果在堆上创建对象,为其分配堆内存
- if (本对象在堆上)
- {
- this = ::operator new(sizeof(Derived));
- }
- // 初始化父类对象
- Base::Base();
- s3.string(); // 构造s3
- s4.string(); // 构造s4
- s5.string(); // 构造s5
- }
如果string的构造函数也恰巧被内联,Derived的构造函数将得到其代码的5个拷贝(2个继承而来,3个自己声明)。现在你应该明白,内联Derived的构造函数并非可以很简单就决定的;类似的情况也适用于析构。
4、内联函数只适合于只有1~5行的小函数。
四、内联还得面临2个头疼的问题
第一,该如何维护?一个函数开始的时候可能满足了设定的内联标准,并以内联的形式出现,但随着系统的扩展升级,函数体中增添了一些新功能,函数体变得复杂了,这就使内联函数不再适合内联,此时需要把函数前面的inline去掉,并把函数体放到相应的源文件中,这会使维护的难度有所增加。
第二,不可避免的重新编译。当内联函数实现改变的时候,用户必须重新编译他们的代码以反映出这种改变。而对于非内联函数,我们仅仅需要重新链接。
所以,使用inline需要依靠积累的经验来进行判断,合理正确的使用内联,可以提高程序的性能,反之,会带来意想不到的副作用!