但是天下没有免费的午餐,inline函数在潇洒背后也有辛酸
在一台内存有限的机器上,过度热衷inlining会造成程序体积太大,即使拥有虚拟内训,inline造成的代码膨胀亦会导致额外的换页行为(paging),降低指令高速缓存装置的命中率,以及伴随这些而来的效率损失。这一点尤其在目前炙手可热的移动开发来说很重要。
换个角度说,如果inline函数的本体很小,编译器针对“函数本体”所产出的码可能比针对“函数调用”所产出的码更小,果真如此,将函数inlining确实可能导致较小的目标码和较高的指令高速缓存命中率。
需要注意的是:inline只是对编译器的一个申请,而不是强制命令。这项申请可以隐喻提出,也可以明确提出。隐喻方式是将函数定义于class定义式内:
class Person {
public:
...
int age() const {return theAge;} //一个隐喻的inline申请,age被定义于class定义式内
...
private:
int theAge;
};
明确声明inline函数的做法则是在其定义式前加上关键字inline,例如标准的max template的实现就是:
template
inline const T& std::max(const T& a, const T& b) //一个明确的inline申请,std::max之前有关键字“inline”
{ return a > b ? b : a;}
大部分编译器拒绝将太过复杂(例如带有循环或递归)的函数inlining,而所有对virtual函数的调用(除非是最平淡无奇的)也都会使inlining落空,因为virtual意味着“等待,直到运行期才确定调用哪个函数”,而inline意味着“执行前,先将调用动作替换为被调用函数的本体”。如果编译器不知道该调用哪个函数,那就别怪它无情地拒绝将函数本体inlining了
还是那句话天下没有免费的午餐,inline函数带来好处的同时也会有副作用:inline函数无法随着程序库的升级而升级。换句话说:如果某个函数f是程序库内的一个inline函数,客户将”f函数本体“编进其程序中了,一旦程序库设计者决定改变f,所有用到f的客户端程序都必须重新编译。如果f是non-inline函数,且在动态库中,则升级版函数会不知不觉地被应用程序吸纳。此外,有很多调试器不支持在inline函数中设置断点,因为此时函数并不存在。
总结来了