Effective C++ 条款30 透彻了解inlining的里里外外

1. inline函数既和带参宏一样不带来函数调用的额外开销,又具有和非inline函数相同的功能,也就是说,inline函数同时具备带参宏和非inline函数的优点.

    此外,编译器优化机制通常针对于那些不含参数调用的代码,因此inline某个函数就有可能使编译器对它执行语句相关最优化.

2. 虽然inline函数有诸多优点,但由于inline函数对每一次函数调用都用函数本体替换,这无疑加重了编译负担,更重要的是它增加了代码量,此外,由于inline造成的代码膨胀"会导致额外的换页行为(paging),降低指令高速缓存装置的击中率(instruction cache hit rate)",以致增加效率损失.当然,如果inline函数的本体很小,编译器所产出的码可能比函数调用的码更小,将函数inline反而可以导致较小的目标码和较高的指令高速缓存装置击中率.

    此外,将函数声明为inline可能会带来其他副作用:"inline函数无法随着程序库的升级而升级",如果程序库内的某个inline函数需要被更改,那么所有用到该函数的代码都需要被重新编译(如果该函数是非inline函数,只要函数借口不变,客户端只需重新链接即可.如果使用动态链接,"升级版函数甚至可以不知不觉地被应用程序吸纳").

3. inline只是对编译器的一个申请,并不是强制命令,编译器对代码量复杂的函数(例如循环或递归),virtual函数(必须在执行期才能决定本体)拒绝展开(并会产生警告).此外,inline函数也可以隐喻提出,在类中定义的函数默认被声明为inline(由于友元函数也可以在类内部定义,这样的友元函数也被声明为inline函数).

    注:头文件中只能有声明,而不能有定义,只有三个例外:类定义,inline函数,const变量.只有类的前置声明无法构造类对象,因此头文件中一定要有类的定义,inline函数类似,要将函数展开就一定要看到函数本体,因此inline函数通常被置于头文件中.

4. 即使编译器同意inline某个函数,但有时同样也会为该函数生成一个函数本体,例如程序要取一个inline函数的地址,此时编译器虽然将函数inline,但还是为产生了本体.此外,编译器通常不对"通过函数指针进行的调用"实施inlining,这意味着"对inline函数的调用有可能被inline,也有可能不被inline,取决于该调用的实施方式".即使不使用函数指针,编译器依然可能为构造函数和析构函数生成outline副本,以便于获取指针而在array内部元素的构造和析构中使用

5. 此外,有些函数看起来代码量很小,但编译器可能在其中插入了大量代码(为了支持面向对象这经常发生),例如对于一个继承层次中的底层派生类,其构造函数需要包含整个继承层次中的基类的构造函数以及它的成员对象的构造函数,这些都是在编译器被插入的.同理,析构函数也是如此.因此,是否将构造函数和析构函数inline也应当被慎重考虑.

6. 除了以上所提,有一个事实更加重要:"大部分调试器对inline函数束手无策",毕竟无法在一个不存在的函数内设置断点(break-point),"虽然有些某些建置环境勉力支持对inlined函数的调试,其他许多建置环境仅仅只能'在调试版程序中禁止发生inlining"'.因此一开始不要将任何函数都声明为inline,或至少仅将那些"一定要成为inline"或"十分平淡无奇"的函数神上,尽管这也将自己推向手工最优化之路.

原文地址:https://www.cnblogs.com/reasno/p/4777307.html