条款30:透彻了解inlining的里里外外

1、inline函数的优点

  • 看起来像函数
  • 动作像函数
  • 比宏要好
  • 没有函数调用所需要的开销
  • 可以享受编译器语境的优化(一般编译器不会对outlined函数调用执行最优化 )

2、inline的函数缺点

可能造成程序体积较大,即使拥有虚拟内存,inling的代码膨胀也会导致额外的换页行为,降低高速缓存装置的击中率,以及伴随而来的效率损失。

3、关于inline的一些规则

总规则:inline只是对编译器的一个申请,不是强制命令。

规则一:inline的两种方式:隐喻方式,明确提出方式。

隐喻方式,即在class声明内定义函数,这样的函数通常是成员函数。friend 函数也可以写在class定义内。
明确声明,即在函数定义前加关键字inline。

规则二:什么函数不会被inline
  • 太过复杂的函数(例如:带有循环或者递归的函数)
  • 所有的virtual函数。(virtual运行期才能知道调用哪个版本,而大部分编译器inline是在编译器完成的。)

注意:一般你申请inline的函数,编译器没有实现inline的话,会给出一条警告信息。

规则三:什么时候编译器会既inline函数,又生成outline版本的函数
  • 情况一:你使用指针调用了该函数
  • 情况二:你没有使用指针调用该函数,但是编译器生成的函数使用指针(例如:构造和析构函数)调用了该函数
规则四:为什么派生类的构造函数和析构函数通常并不是inline很好的候选者

因为C++在对象创建和销毁时做了各种各样的保证,例如一个对象创建了一半后引发了异常,那么C++需要把创建好的那部分给析构掉。这实际上都是有代码在执行的,这些编译器自己生成的代码往往被编译器放在构造函数和析构函数中。而一个派生类就容易发生这样的异常。因此,派生类的构造和析构函数并不是inline的很好的候选者。

规则五:为什么基类的构造函数和析构函数通常并不是inline很好的候选者

如果base构造函数被写成inline 的,那么所有的替换base构造函数的代码会被插入到派生类构造函数的调用内。

4、对于inline的一个误解

(1)inline和function template是没关系的

由于function template函数和inline函数一般都位于头文件内,误使得我们认为function template一定是inline 的。这完全是错误的,inline和function template是没关系的。 下面解释为什么二者通常都放在头文件内。

(2)为什么inline函数需要放在头文件内?

大多数的编译环境在编译期过程中进行inling,而为了将一个“函数调用”替换为“被调用函数本体”,编译器需要知道函数长什么样。(有些特别的编译环境在连接器甚至是运行期进行inling,但是大多数编译器都是在编译期。)

(3)为什么function Templates 通常也被置于头文件内?

因为它一旦被使用,编译器为了将它局现化,需要知道它长什么样子。(但是Templates 的具现化也不一定都在编译期,有些在连接期。)

(4)什么时候function template需要inline?

如果你写的function template 具现化的函数都应该是inlined的,那么需要将此function template 写成inline形式。如果你写的function template 具现化的函数不都是inlined 的就不应该写成inline形式。

5、inline带来的其他影响

(1)程序库设计者必须评估将函数声明为inline 的冲击:代码升级
  • inline函数无法随着程序库的升级而升级。也就是说一样inline函数升级了,所有用到inline函数的客户端代码都要重新编译。
  • 但是如果此函数时outinlined 那么只需要重新连接就行了
  • 如果此函数是动态连接的,那么可以无声无息的被应用程序吸纳
(2)程序开发人员对于inline 的注意:代码调试

大部分调试器都对于inline函数束手无策,例如你不能在一个不存在的函数内设置断点。大部分调试器禁止在调试程序中发生inlining。

6、最后:对于inline的整体策略

  • 一开始不要写任何inline,或者说一定可以inline的才inline。
  • 然后根据20-80法则,对于20那部分代码,竭尽所能的将其inline或瘦身,以提高代码速度。
原文地址:https://www.cnblogs.com/lasnitch/p/12764164.html