简介
内联函数,看起来像是函数,运作起来像是函数,比宏要好得多(宏没有错误检查等缺点),使用时还不需要承担函数调用的开销。
基本思想:
将每个函数调用以它的代码来替换。
但这种做法很可能会增加整个目标代码的体积。在一台内存有限的计算机里,过分地使用内联所产生的程序会因为有太大的体积导致可用空间不够。即使可以使用虚拟内存,内联造成的代码膨胀也可能会导致不合理的页面调度行为(系统颠簸),这将使你的程序运行慢得象在爬。(当然,它也为磁盘控制器提供了一个极好的锻炼方式:))过多的内联还会降低指令高速缓存的命中率,从而使取指令的速度降低,因为从主存取指令当然比从缓存要慢。
另一方面,如果内联函数体非常短,编译器为这个函数体生成的代码就会真的比为函数调用生成的代码要小许多。如果是这种情况,内联这个函数将会
确实带来更小的目标代码和更高的缓存命中率!
inline 指令就象 register,它只是对编译器的一种提示,而不是命令。也就是说,只要编译器愿意,它就可以随意地忽略掉你的指令,事实上编译器常常会这么做。例如,大多数编译器拒绝内联"复杂"的函数(例如,包含循环和递归的函数);还有,即使是最简单的虚函数调用,编译器的内联处理程序对它也爱莫能助。(这一点也不奇怪。virtual 的意思是"等到运行时再决定调用哪个函数",inline 的意思是"在编译期间将调用之处用被调函数来代替",如果编译器甚至还不知道哪个函数将被调用,当然就不能责怪它拒绝生成内联调用了)。
一个给定的内联函数是否真的被内联取决
于所用的编译器的具体实现。幸运的是,大多数编译器都可以设置诊断级,当
声明为内联的函数实际上没有被内联时,编译器就会为你发出警告信息
旧标准的内联
如果在一个头文件中定义一个内联函数,但编译器并不将其作为内联,称其为被外联的内联。当cpp文件包含该头文件被编译时,生成的目标文件中将包含一个该函数,就像该函数没有被声明为inline一样,但是将多个这样的cpp文件的目标文件链接在一起时,编译器就会因为程序中有两个同名的函数定义而报错。
为了防止该问题,旧规则规定,对于未被内联的内联函数,编译器将其当成被声明为static那样处理,即,使他局限于当前被编译得文件。这一策略消除了链接时的错误,但带来了开销,每个包含该函数的被编译单元都包含有自己的该函数的静态拷贝。并且,当该函数内部定义了局部静态变量,那么,每一个该函数的拷贝都有此局部变量的一份拷贝(一般来说,函数中的static意味着只有一份拷贝)。
某些情况下,编译器将内联函数确实内联了,此时,如果程序中要取一个内联函数的地址,编译器就必须为此生成一个函数体(毕竟内联函数是替换,不是真的函数)。这在旧的规则下,每个取用内联函数地址的被编译单元还是各自生成了此函数的静态拷贝。
新标准的内联
假设写了某个函数 f 并声明为 inline,如果出于什么原因,编译器决定不对
它内联,那将会发生些什么呢?最明显的一个回答是将 f 作为一个非内联函数
来处理:为 f 生成代码时就象它是一个普通的"外联"函数一样, 对 f 的调用也
象对普通函数调用那样进行。
而且当程序中需要使用内联函数的地址时,不管涉及的被编译单元有多少,将只生成唯一一个内联函数的外部拷贝。这种特性加到C++标准的时间相对较晚,但现在的编译器基本都支持。
正确地使用内联
找出程序中那些重要的函数(二八法则:80%的时间运行的20%的代码),以及哪些内联后确实可以提高程序性能的函数,将其声明为内联。同时,还需要注意代码膨胀带来的问题,并监视编译器的警告信息,看看内联函数是否真的被编译器内联。