根据我所读的内容,编译器没有义务用其主体替换内联函数的函数调用,但是如果可以的话,它将这样做。这让我开始思考-如果是这种情况,为什么我们要使用内联词?为什么不将所有函数默认设置为内联函数,并让编译器确定它是否可以用函数主体替代调用?
根据我所读的内容,编译器没有义务用其主体替换内联函数的函数调用,但是如果可以的话,它将这样做。这让我开始思考-如果是这种情况,为什么我们要使用内联词?为什么不将所有函数默认设置为内联函数,并让编译器确定它是否可以用函数主体替代调用?
Answers:
inline
来自C;它对C ++来说并不是什么新鲜事物。
有C个关键字(register
和inline
)被设计为允许程序员帮助代码优化。如今,这些通常被忽略,因为编译器可以在寄存器分配和决定何时内联函数方面做得更好(实际上,编译器可以在不同时间内联或不内联函数)。与Ritchie发明C语言时常见的确定性代码相比,现代处理器上的代码生成要复杂得多。
在C ++中,该词现在的含义是它可以具有多个相同的定义,并且需要在使用它的每个翻译单元中进行定义。(换句话说,您需要确保它可以被内联。)您可以拥有一个inline
在标头中包含一个没有问题的函数,并且在类定义中定义的成员函数会自动有效地实现inline
。
inline
。
inline
是在C ++ 中进行了标准化,尽管它已经可以作为C中的供应商扩展来使用。。。是的,似乎它已被添加到C99中的C标准中。
inline
C99和更高版本中的规则inline
与C ++中的规则不同。
最初inline
是一个非常强烈的暗示,即应该内联对函数的调用。
但是,唯一保证的效果inline
是允许在多个转换单元中(有效地相同)定义一个函数,例如,将定义放在头文件中。
如今,一些编译器非常热衷于遵循内联提示,例如g ++。还有一些编译器对此不太重视,例如Visual C ++。但是所有人都必须遵守保证。
不幸的是,这两个含义(优化提示和我们可以称为链接器级可丢弃定义)与同一个关键字驻留在一起,因为这意味着您几乎不可能没有另一个。
同样不幸的是,inline
(或者更好的是,一个关于可废弃定义的单独关键字)不能应用于数据。
随着仅标头模块的普及,对链接器级可丢弃数据的需求日益增加。例如,许多Boost子库都是仅标头的。
对于数据,您可以对模板应用一些技巧。在某些类模板中定义它,提供一个typedef
with模板参数void
(或其他参数)。这是因为“一个定义规则”对模板有特定的例外。
inline
。
为什么不将所有函数默认设置为内联?因为这是工程上的折衷。至少有两种类型的“优化”:加速程序并减小程序的大小(内存占用)。内联通常会加快处理速度。它消除了函数调用的开销,避免了从堆栈中压入然后拉出参数。但是,这也使程序的内存占用量更大,因为现在必须使用函数的完整代码替换每个函数调用。要使事情变得更加复杂,请记住,CPU将频繁使用的内存块存储在CPU的高速缓存中,以进行超快速访问。如果您使程序的内存映像足够大,则您的程序将无法有效地使用高速缓存,并且在最坏的情况下,内联实际上会降低程序的速度。
要了解“内联”,您需要 了解历史以及20(和30)年前的生活。
我们在内存很少的计算机上编写代码,因此编译器不可能一次性处理组成程序的所有代码。编译器的运行速度也很慢,因此您不需要重新编译未更改的代码–花费24个小时(在价格超过高端汽车的计算机上)重新编译所有代码对于某些项目来说是正常的从事。
因此,每个代码文件被分别编译成一个目标文件。每个目标文件均以其包含的所有功能的列表以及功能的“地址”开始。一个目标文件还包含它在其他目标文件中调用的所有函数的列表以及该调用的位置。
一个链接将先读所有的目标文件,并建立自己的出口,与他们在文件中有地址以及所有功能的列表。然后它将重新读取所有目标文件,将它们输出到程序文件,同时使用函数的地址更新所有“外部”函数调用。
链接器除了修改对外部函数调用的引用以外,没有以任何其他方式更改或优化编译器生成的机器代码。 链接器是操作系统的一部分,并且早于大多数编译器。当人们编写新的编译器时,他们需要它与当前的链接器一起使用,并能够链接到当前的目标文件,否则无法进行系统调用。
编译器只看到正在编译的“ .c”或“ .cpp”文件中的代码以及所有包含的头文件。因此,它无法基于其他“ .c”或“ .cpp”文件中的代码进行任何优化。
“ inline”关键字允许在头文件中定义函数(方法)的主体,因此允许编译器在编译调用该函数的代码时使用该函数的代码。例如,假设您在另一个.cpp文件中定义了一个收集类,则该类将具有一个“ isEmpty”方法,该方法包含一行代码,如果代替调用一个函数,则可以大大提高生成的程序的速度,函数调用已替换为这一行。
当时,“ inline”关键字被视为一种“便宜而简单”的方法,可以在不浪费函数调用成本的情况下封装数据,如果没有它,很多程序员将只能访问对象的私有字段。 (在宏中,最糟糕的方式是“内联”代码,而这在当时很普遍。)
如今,“链接器”进行了大量代码优化,并且往往由某些团队作为编译器来编写。编译器通常只是检查代码是否正确,然后“压缩”它,而将大部分机器代码创建任务留给链接器。
让我们看看标准的内容(用黑体突出显示的重要部分):
2.具有内联说明符的函数声明用于声明内联函数。内联说明符向实现指示,在调用点处对函数体进行内联替换比通常的函数调用机制更可取。无需执行即可执行此操作在调用点内联替换的实现;但是,即使省略了此内联替换,仍应遵守内联函数的其他规则。
— C ++标准,ISO / IEC 14882: 2003、7.1.2功能说明符[dcl.fct.spec]
因此,如果您想确定的话,应该阅读编译器的文档。
内联所有内容不是一个好主意,因为它可能导致大量重复的机器代码...
因此,您必须知道:
没有简单的答案:您必须使用它才能看到最好的。不要满足于简单的答案,例如“从不使用
inline
功能”或“始终使用inline
功能”或“使用inline
仅当函数少于N行代码时才。这些适合所有人的规则可能很容易写下来,但它们会产生次优的结果。— C ++常见问题解答,内联函数,9.3
inline
函数是否可以提高性能?
inline
通过重申知识来说明问题,因为OP似乎错过了一部分,这就是他不明白这一点的核心原因。为了获得要点,他需要了解该点之下的内容...
让我给您一个使用inline关键字的充分理由。
在嵌入式系统上,例如票证打印机或类似的较小系统。处理器非常有限,函数调用(在栈上准备函数参数,调用,从栈中获取参数并放回答案等)可能需要花费几毫秒的时间才能执行。
可以说,调用时间约为60毫秒(仅用于调用,而不是实际函数),并且您进行了50次迭代(在树中进行循环或迭代调用)。
从该函数调用来回移动的时间将花费60 * 50 = 3000(3秒)。
如果您有足够的内存,您肯定会内联以节省3秒钟。
因此,内联基本上在需要执行速度时使用。在我参与的一些项目中,调用时间比执行时间长,这是使用内联的典型情况。