读书笔记:Effective C++ 炒冷饭 - Item 30 明明白白内联函数

读书笔记:Effective C++ 炒冷饭 - Item 30 明明白白内联函数
[原创文章欢迎转载,但请保留作者信息]
Justin 于 2010-01-04

使用内联函数(inline function)可以省去一般函数调用的入栈操作开销,比宏(macro)要好用。从编译器的角度来看,没有函数调用的代码要更容易优化。
但是天下没有免费的午餐,以空间换时间的内联函数同时也带来了更大的程序占用空间,更甚者还会因为这变大的代码空间导致额外的内存换页操作,降低指令缓存(instruction cache)的命中率……这些都是使用内联函数需要考虑到的负面影响。(Scott还是辩证地提醒了一点:当内联函数 非常短小时,相比一般意义上的函数调用,它能够帮助编译器生成更小的最终代码和运行时更高的指令缓存命中率)

内联函数的声明可以是显式的:使用inline关键字;也可以是隐式的:在类的定义中定义函数。

内联函数一般而言都是定义在头文件中,这是因为大多数编译器的内联动作都是发生在编译过程中。(也有着链接甚至是运行中才进行内联的,但是俺们这里随大流,讲主要矛盾)
虽然内联函数和函数模板有一点相似:它们都几乎定义在头文件中,但是这两者之间没有必然联系,而非有的程序员想的那样“函数模板一定是内联的”)

另外一点容易被初学者忽视的是,内联函数的定义仅仅是对编译器提出内联的请求。编译器完全有可能忽视这个请求,于是某“内联函数”有可能在最后还是生成了一般函数的代码。在这个情况下编译器很有可能是对的:
  • 请求内联的函数有可能太过于复杂
  • 请求内联的函数有可能是虚函数(虚函数的真正实体要在运行时才能得知,让编译器编译阶段去做内联实在有点强人所难)
  • 请求内联的函数没什么问题,但是在代码中有用函数指针的方式调用该函数(这样编译器也没办法,如果不生成一般函数哪来的函数指针?)
这些情况下确实不应该把函数作为内联函数。一个“内联函数”是否最终生成了内联函数,还得编译器说了算。
然而编译器并不是总能帮助我们做出正确的决定,还有一些情况是需要我们自己做出判断的:
  • 请求内联的函数是构造/析构函数(表面上看起来某个构造/析构函数很短小甚至是空的,但是为了构造/析构类中的其他成员,编译器有可能会“自觉”地写入必要的代码,这样的构造/析构函数就有可能不适合再做内联了)这一点原文中有更详细的说明。
  • 当编写支持库时(library)也不建议使用内联函数,因为一旦用户使用了这些含有内联函数的库并编译了自己的程序,这些内联函数就已经“写死”在他们的程序中了。当日后对原先的库做了更新修改,用户就必须重新编译整个程序才能用上新的补丁。而一般的函数就不会有这个问题:他们是动态链接的,用户根本感觉不到任何改动。
  • 考虑到很多调试器(debugger)无法调试内联函数(本来就没有这么一个“函数”,叫人家怎么设断点?),在调试版本中也不建议使用内联函数。
有那么多需要注意的地方,大师最后总结了一下:用好内联函数的第一步就是:不用内联函数。并没有那么多的函数真正需要内联,因为80%的程序运行时间都是花在了20%的代码中。第二步是把内联函数当成是手工优化的手段,仅仅在非常需要效率和优化的代码中使用内联。

你可能感兴趣的:(读书笔记:Effective C++ 炒冷饭 - Item 30 明明白白内联函数)