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

inlining函数背后的整体观念是, 将“此函数的每一个调用”都以函数本体换之。所以会增加目标码代码.
inlining只是对编译器的一个申请,而不是一个强制命令。这项申请可以隐喻提出也可以明确提出。


隐喻方式:将函数定义于class定义式内,这样的函数通常是成员函数,定义于class定义式内的friend函数
**例:
class Person
{
public:
int age() const {return theAge;}
private:
int theAge;
};
*******************************/


明确方式:函数定义式前加关键字inline
**例
template <typename T>
inline const T& max(const T& a, const T& b)
{
return a < b ? b : a;
}
*******************************/


inline函数通常一定被置于头文件内,因为大多数build环境在编译过程中进行inlining, 而为了将一个函数调用换成其本体,编译器必须知道那个函数长什么样子。
template通常也被置于头文件内,因为编译器为了将它具像化,需要知道它长什么样。

大部分编译器拒绝将太过复杂的(带递归和循环)函数inlining,而对virtual函数的inlining也会落空,因为virtual意味着执行时决定用哪个函数,而inlining在编译时就需要知道函数本体


有时编译器可能有意愿inlining某个函数,但仍会为该函数生成本体
**例
inline void f(...){} //假设编译器有意愿inlining函数f
void (*pf)() = f; //pf指向f
f(); //这将被inlining,因为是正常调用
pf(); //这个调用或许不被inlining, 因为它通过函数指针达成
****************************/

不要inlining构造函数和析构函数,因为他们有很多默认执行操作

!!! inline函数无法随着程序升级库的升级而升级。 如果函数f被inline,那么当f本体改变时,所有用到f的地方都要重新编译。
 而如果f是non-inline函数,一旦重新修改,重新连接就好。如果程序库采用动态连接,升级版函数甚至可以不知道不觉被应用程序采纳
!!! 大部分调试器面对inline函数都束手无策,因为无法对不存在的函数本体断点。
请记住:

》将大多数inlining限制在小型,被频繁调用的函数身上,这可使日后的调试过程和二进制升级更容易,也可使潜在的代码膨胀问题最小化,使程序的速度提升机会最大化。

》不要只因为function templates出现在头文件,就将它们声明为 inline

猜你喜欢

转载自blog.csdn.net/u010731020/article/details/81031454