C ++中的内联函数。重点是什么?


19

根据我所读的内容,编译器没有义务用其主体替换内联函数的函数调用,但是如果可以的话,它将这样做。这让我开始思考-如果是这种情况,为什么我们要使用内联词?为什么不将所有函数默认设置为内联函数,并让编译器确定它是否可以用函数主体替代调用?


Answers:


40

inline来自C;它对C ++来说并不是什么新鲜事物。

有C个关键字(registerinline)被设计为允许程序员帮助代码优化。如今,这些通常被忽略,因为编译器可以在寄存器分配和决定何时内联函数方面做得更好(实际上,编译器可以在不同时间内联或不内联函数)。与Ritchie发明C语言时常见的确定性代码相比,现代处理器上的代码生成要复杂得多。

在C ++中,该词现在的含义是它可以具有多个相同的定义,并且需要在使用它的每个翻译单元中进行定义。(换句话说,您需要确保它可以被内联。)您可以拥有一个inline在标头中包含一个没有问题的函数,并且在类定义中定义的成员函数会自动有效地实现inline


3
+1的历史记录inline
Tamara Wijsman

11
我很确定这首先inline是在C ++ 中进行了标准化,尽管它已经可以作为C中的供应商扩展来使用。。。是的,似乎它已被添加到C99中的C标准中。
Ben Voigt

@Ben Voigt:你是对的。我第一次遇到它C.
大卫·索恩利

5
还应注意,不仅历史记录是错误的,而且inlineC99和更高版本中的规则inline与C ++中的规则不同。
Alf P. Steinbach

27

最初inline是一个非常强烈的暗示,即应该内联对函数的调用。

但是,唯一保证的效果inline是允许在多个转换单元中(有效地相同)定义一个函数,例如,将定义放在头文件中。

如今,一些编译器非常热衷于遵循内联提示,例如g ++。还有一些编译器对此不太重视,例如Visual C ++。但是所有人都必须遵守保证。

不幸的是,这两个含义(优化提示和我们可以称为链接器级可丢弃定义)与同一个关键字驻留在一起,因为这意味着您几乎不可能没有另一个。

同样不幸的是,inline(或者更好的是,一个关于可废弃定义的单独关键字)不能应用于数据

随着仅标头模块的普及,对链接器级可丢弃数据的需求日益增加。例如,许多Boost子库都是仅标头的。

对于数据,您可以对模板应用一些技巧。在某些类模板中定义它,提供一个typedefwith模板参数void(或其他参数)。这是因为“一个定义规则”对模板有特定的例外。

注意:C ++ 17支持
¹ inline变量。


1
+1获取有关的更多信息inline
Tamara Wijsman

1
有效地相同 ”实际上是一个非常棘手的条件,因为设计得很差的C ++语言会努力使通常能干和细心的程序员编写使用静态对象的合理但形式上未定义的代码-我想知道有多少委员会成员自己陷入了陷阱
curiousguy

6

为什么不将所有函数默认设置为内联?因为这是工程上的折衷。至少有两种类型的“优化”:加速程序并减小程序的大小(内存占用)。内联通常会加快处理速度。它消除了函数调用的开销,避免了从堆栈中压入然后拉出参数。但是,这也使程序的内存占用量更大,因为现在必须使用函数的完整代码替换每个函数调用。要使事情变得更加复杂,请记住,CPU将频繁使用的内存块存储在CPU的高速缓存中,以进行超快速访问。如果您使程序的内存映像足够大,则您的程序将无法有效地使用高速缓存,并且在最坏的情况下,内联实际上会降低程序的速度。


5
但是,编译器可以(而且确实可以!)比一般程序员更好地解决这一问题。因此,这不是有效的参数。
康拉德·鲁道夫

现在必须用函数的完整代码替换每个函数调用 ”并且,内联函数不是一个省略了调用序列并且将函数的汇编代码复制到位的函数。通过内联高级中间代码,就可以编译内联函数。这样一来,编译器就可以将函数体有效地视为一个干净的宏(一个干净的宏是没有C预处理程序宏的怪异功能),并且可能有许多可用的优化方法。
curiousguy

3

要了解“内联”,您需要 了解历史以及20(和30)年前的生活。

我们在内存很少的计算机上编写代码,因此编译器不可能一次性处理组成程序的所有代码。编译器的运行速度也很慢,因此您不需要重新编译未更改的代码–花费24个小时(在价格超过高端汽车的计算机上)重新编译所有代码对于某些项目来说是正常的从事。

因此,每个代码文件被分别编译成一个目标文件。每个目标文件均以其包含的所有功能的列表以及功能的“地址”开始。一个目标文件还包含它在其他目标文件中调用的所有函数的列表以及该调用的位置。

一个链接将先读所有的目标文件,并建立自己的出口,与他们在文件中有地址以及所有功能的列表。然后它将重新读取所有目标文件,将它们输出到程序文件,同时使用函数的地址更新所有“外部”函数调用。

链接器除了修改对外部函数调用的引用以外,没有以任何其他方式更改或优化编译器生成的机器代码。 链接器是操作系统的一部分,并且早于大多数编译器。当人们编写新的编译器时,他们需要它与当前的链接器一起使用,并能够链接到当前的目标文件,否则无法进行系统调用。

编译器只看到正在编译的“ .c”或“ .cpp”文件中的代码以及所有包含的头文件。因此,它无法基于其他“ .c”或“ .cpp”文件中的代码进行任何优化。

“ inline”关键字允许在头文件中定义函数(方法)的主体,因此允许编译器在编译调用该函数的代码时使用该函数的代码。例如,假设您在另一个.cpp文件中定义了一个收集类,则该类将具有一个“ isEmpty”方法,该方法包含一行代码,如果代替调用一个函数,则可以大大提高生成的程序的速度,函数调用已替换为这一行。

当时,“ inline”关键字被视为一种“便宜而简单”的方法,可以在不浪费函数调用成本的情况下封装数据,如果没有它,很多程序员将只能访问对象的私有字段。 (在宏中,最糟糕的方式是“内联”代码,而这在当时很普遍。)

如今,“链接器”进行了大量代码优化,并且往往由某些团队作为编译器来编写。编译器通常只是检查代码是否正确,然后“压缩”它,而将大部分机器代码创建任务留给链接器。


2

让我们看看标准的内容(用黑体突出显示的重要部分):

2.具有内联说明符的函数声明用于声明内联函数。内联说明符向实现指示,在调用点处对函数体进行内联替换比通常的函数调用机制更可取无需执行即可执行此操作在调用点内联替换的实现;但是,即使省略了此内联替换,仍应遵守内联函数的其他规则。

C ++标准,ISO / IEC 14882: 2003、7.1.2功能说明符[dcl.fct.spec]

因此,如果您想确定的话,应该阅读编译器的文档。

内联所有内容不是一个好主意,因为它可能导致大量重复的机器代码...

因此,您必须知道:

没有简单的答案:您必须使用它才能看到最好的。不要满足于简单的答案,例如“从不使用inline功能”或“始终使用inline功能”或“使用inline仅当函数少于N行代码时才。这些适合所有人的规则可能很容易写下来,但它们会产生次优的结果。

C ++常见问题解答,内联函数,9.3 inline函数是否可以提高性能?


1
您说的是正确的,但并不相关,因为作为程序员,您是否要内联不是您的决定。您放置关键字,您可能会或可能不会内联函数。您不输入关键字,仍然可以或不可以将它们内联。当您输入关键字时,具有任何规则都是毫无意义的,而实际上由编译器来决定的是什么规则。
凯特·格雷戈里

如果与您说的完全一样,这有什么关系呢?没有简单的答案
Tamara Wijsman

这无关紧要,因为它不能回答OP的问题。他不是在问“内联做什么”,而是在问“重点是什么”。您的回答只是重申了我们对内联的全部了解。
2011年

@Davor:“有什么意义?” 不遵守FAQ,因此我正在回答问题中提出的问题。我inline通过重申知识来说明问题,因为OP似乎错过了一部分,这就是他不明白这一点的核心原因。为了获得要点,他需要了解该点之下的内容...
Tamara Wijsman

为什么不遵守FAQ?标准描述了内联关键字的语法和语义,OPs的目的是什么,何时使用它,应该解决什么问题?我看不出这与FAQ不一致。
DavorŽdralo2011年

1

让我给您一个使用inline关键字的充分理由。

在嵌入式系统上,例如票证打印机或类似的较小系统。处理器非常有限,函数调用(在栈上准备函数参数,调用,从栈中获取参数并放回答案等)可能需要花费几毫秒的时间才能执行。

可以说,调用时间约为60毫秒(仅用于调用,而不是实际函数),并且您进行了50次迭代(在树中进行循环或迭代调用)。

从该函数调用来回移动的时间将花费60 * 50 = 3000(3秒)。

如果您有足够的内存,您肯定会内联以节省3秒钟。

因此,内联基本上在需要执行速度时使用。在我参与的一些项目中,调用时间比执行时间长,这是使用内联的典型情况。


嗯什么 将内联放入代码中并不意味着编译器实际上会对其内联,不将其放入其中也并不意味着不会。这只是一个提示,如今的编译器通常将其忽略。如果编译器发现内联很有用,那么它将不会,否则将不会。您在那里没有真正的控制权。OP已经知道这一点,并在问,我引用:“这是什么意思?”。谁给这个+1?这既有误导性(暗示关键字inline会强制内联)又与问题无关。
2011年

2
@DavorŽdralo无需粗鲁。我将问题解释为为什么应该使用内联?我的观点是表明可以更快地获取代码,但要消耗内存。每个编译器对inline关键字的处理方式可能不同,因此您需要查看文档(我没有提到)。由于您不能总是负担内联创建的额外内存开销,因此“指导”何时使用它很有用。我也没有说内联是强制执行的。有人认为这个答案对他/她有用,并对其投了+1,请尊重其他人可能有另一种经验,并找到一些有用的东西。
Max Kielland
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.