269

在 C++ 中使用内联函数的优点/缺点是什么?我看到它只会提高编译器输出的代码的性能,但是对于今天优化的编译器、快速的 CPU、巨大的内存等(不像 1980 年那样,内存稀缺,所有东西都必须适合 100KB 的内存)什么他们今天真的有优势吗?

4

14 回答 14

208

好处

  • 通过在需要的地方内联代码,您的程序将在函数调用和返回部分上花费更少的时间。它应该使您的代码运行得更快,即使它变得更大(见下文)。内联普通访问器可能是有效内联的一个例子。
  • 通过将其标记为内联,您可以将函数定义放在头文件中(即它可以包含在多个编译单元中,而链接器不会抱怨)

缺点

  • 它可以使您的代码更大(即,如果您将内联用于非平凡函数)。因此,它可能会引发分页并破坏编译器的优化。
  • 它稍微破坏了您的封装,因为它暴露了您的对象处理的内部(但是,每个“私人”成员也会这样做)。这意味着您不能在 PImpl 模式中使用内联。
  • 它稍微破坏了您的封装 2:C++ 内联在编译时解决。这意味着如果您更改内联函数的代码,您将需要使用它重新编译所有代码以确保它会被更新(出于同样的原因,我避免函数参数的默认值)
  • 在头文件中使用时,它会使您的头文件变大,因此会用用户不关心的代码稀释有趣的信息(如类方法列表)(这就是我在 a 中声明内联函数的原因类,但将在类主体之后的标题中定义它,而不是在类主体内)。

内联魔术

  • 编译器可能会也可能不会内联您标记为内联的函数;它还可能决定在编译或链接时内联未标记为内联的函数。
  • 内联的工作方式类似于由编译器控制的复制/粘贴,这与预处理宏有很大的不同:宏会被强制内联,会污染所有的命名空间和代码,不容易调试,甚至会被完成如果编译器会裁定它效率低下。
  • 在类本身内部定义的类的每个方法都被认为是“内联的”(即使编译器仍然可以决定不内联它
  • 虚拟方法不应该是可内联的。尽管如此,有时,当编译器可以确定对象的类型时(即对象是在同一个函数体内声明和构造的),即使是虚函数也会被内联,因为编译器确切地知道对象的类型。
  • 模板方法/函数并不总是内联(它们在标题中的存在不会使它们自动内联)。
  • “内联”之后的下一步是模板元编程。即通过在编译时“内联”您的代码,有时,编译器可以推断出函数的最终结果......因此,有时可以将复杂的算法简化为一种return 42 ;语句。这对我来说是极端的内联。它在现实生活中很少发生,它使编译时间更长,不会使您的代码膨胀,并使您的代码更快。但是就像圣杯一样,不要试图将它应用到任何地方,因为大多数处理都无法通过这种方式解决......不过,无论如何这很酷......
    :-p
于 2008-09-28T14:37:39.917 回答
150

内联函数更快,因为您不需要像参数和返回地址那样在堆栈上/从堆栈上推送和弹出东西;但是,它确实使您的二进制文件稍大一些。

它有很大的不同吗?对于大多数人来说,在现代硬件上不够明显。但它可以有所作为,这对某些人来说已经足够了。

将某些内容标记为内联并不能保证它是内联的。这只是对编译器的建议。有时这是不可能的,例如当你有一个虚函数时,或者当涉及到递归时。有时编译器只是选择不使用它。

我可以看到这样的情况会产生可察觉的差异:

inline int aplusb_pow2(int a, int b) {
  return (a + b)*(a + b) ;
}

for(int a = 0; a < 900000; ++a)
    for(int b = 0; b < 900000; ++b)
        aplusb_pow2(a, b);
于 2008-09-28T13:36:29.953 回答
45

在古老的 C 和 C++ 中,inline就像register:向编译器提出关于可能的优化的建议(无非是建议)。

在现代 C++ 中,inline告诉链接器,如果在不同的翻译单元中找到多个定义(不是声明),它们都是相同的,链接器可以自由地保留一个并丢弃所有其他的。

inline如果在头文件中定义了一个函数(无论多么复杂或“线性”),则它是强制性的,以允许多个源包含它而不会导致链接器出现“多重定义”错误。

默认情况下,在类中定义的成员函数是“内联”的,模板函数也是如此(与全局函数相反)。

//fileA.h
inline void afunc()
{ std::cout << "this is afunc" << std::endl; }

//file1.cpp
#include "fileA.h"
void acall()
{ afunc(); }

//main.cpp
#include "fileA.h"
void acall();

int main()
{ 
   afunc(); 
   acall();
}

//output
this is afunc
this is afunc

请注意将 fileA.h 包含到两个 .cpp 文件中,从而产生两个 .cpp 实例afunc()。链接器将丢弃其中之一。如果没有inline指定,链接器将抱怨。

于 2011-09-14T14:47:08.367 回答
17

内联是对编译器的建议,可以随意忽略。它非常适合少量代码。

如果您的函数是内联的,则它基本上是插入到对其进行函数调用的代码中,而不是实际调用单独的函数。这有助于提高速度,因为您不必进行实际通话。

它还协助 CPU 进行流水线操作,因为它们不必使用由调用引起的新指令重新加载流水线。

唯一的缺点是可能会增加二进制大小,但只要函数很小,这不会太重要。

我现在倾向于将这些决定留给编译器(好吧,无论如何都是聪明的)。编写它们的人往往对底层架构有更详细的了解。

于 2008-09-28T13:44:15.630 回答
13

内联函数是编译器使用的优化技术。可以简单地将 inline 关键字添加到函数原型以使函数内联。内联函数指示编译器在代码中使用该函数的任何位置插入完整的函数体。

好处 :-

  1. 它不需要函数调用开销。

  2. 它还节省了函数调用时变量压入/弹出堆栈的开销。

  3. 它还节省了函数返回调用的开销。

  4. 它通过利用指令缓存增加了引用的局部性。

  5. 如果指定,内联编译器还可以应用程序内优化。这是最重要的一个,这样编译器现在可以专注于死代码消除,可以更加强调分支预测,归纳变量消除等。

要了解更多信息,可以点击此链接 http://tajendrasengar.blogspot.com/2010/03/what-is-inline-function-in-cc.html

于 2011-09-14T03:43:36.977 回答
6

我想补充一点,当您构建共享库时,内联函数至关重要。如果不标记函数内联,它将以二进制形式导出到库中。如果导出,它也将出现在符号表中。另一方面,内联函数既不会导出到库二进制文件,也不会导出到符号表。

当库打算在运行时加载时,这可能很关键。它也可能会命中二进制兼容的库。在这种情况下,不要使用内联。

于 2010-05-07T02:59:19.203 回答
4

在优化期间,即使您没有标记函数,许多编译器也会内联函数。如果您知道编译器不知道的内容,通常只需要将函数标记为内联,因为它通常可以自己做出正确的决定。

于 2008-09-28T15:53:06.007 回答
4

inline允许您将函数定义放在一个头文件中,并将#include该头文件放在多个源文件中,而不会违反一个定义规则。

于 2011-09-14T10:03:19.537 回答
3

一般来说,现在任何现代编译器担心内联任何东西都是浪费时间。编译器实际上应该通过自己对代码的分析以及您对传递给编译器的优化标志的规范为您优化所有这些考虑因素。如果您关心速度,请告诉编译器优化速度。如果您关心空间,请告诉编译器优化空间。正如另一个答案所暗示的那样,一个体面的编译器甚至会自动内联,如果它真的有意义的话。

此外,正如其他人所说,使用内联并不能保证内联任何内容。如果你想保证它,你将不得不定义一个宏而不是一个内联函数来做到这一点。

何时内联和/或定义宏以强制包含?- 仅当您对已知会影响应用程序整体性能的关键代码部分的速度有明显且必要的证明时。

于 2008-09-28T16:02:47.840 回答
3

这不仅仅与性能有关。C++ 和 C 都用于嵌入式编程,位于硬件之上。例如,如果您要编写中断处理程序,则需要确保代码可以立即执行,而无需交换额外的寄存器和/或内存页面。这时候 inline 就派上用场了。当需要速度时,好的编译器会自己做一些“内联”,但“内联”会强制它们。

于 2013-12-10T10:21:34.073 回答
1

将函数内联到 so 库中时遇到了同样的麻烦。似乎内联函数没有编译到库中。因此,如果可执行文件想要使用库的内联函数,链接器会发出“未定义的引用”错误。(发生在我用 gcc 4.5 编译 Qt 源代码时。

于 2014-01-08T14:59:39.683 回答
1

为什么不让所有函数默认内联呢?因为这是一个工程权衡。至少有两种“优化”:加速程序和减少程序的大小(内存占用)。内联通常会加快速度。它摆脱了函数调用开销,避免了从堆栈中推入然后拉出参数。但是,它也使程序的内存占用更大,因为现在必须用函数的完整代码替换每个函数调用。更复杂的是,请记住 CPU 将经常使用的内存块存储在 CPU 的高速缓存中以实现超快速访问。如果您使程序的内存映像足够大,您的程序将无法有效地使用缓存,在最坏的情况下,内联实际上可能会减慢您的程序速度。

于 2016-02-28T19:46:57.593 回答
1

我们的计算机科学教授敦促我们不要在 c++ 程序中使用内联。当被问及原因时,他亲切地向我们解释说,现代编译器应该自动检测何时使用内联。

所以是的,内联可以是一种优化技术,可以在任何可能的情况下使用,但显然,只要有可能内联函数,这已经为您完成了。

于 2016-06-17T15:02:59.953 回答
-1

此处另一个讨论的结论:

内联函数有什么缺点吗?

显然,使用内联函数并没有错。

但值得注意的是以下几点!

  • 过度使用内联实际上会使程序变慢。根据函数的大小,内联它可能会导致代码大小增加或减少。内联非常小的访问器函数通常会减少代码大小,而内联非常大的函数会显着增加代码大小。在现代处理器上,由于更好地使用指令缓存,较小的代码通常运行得更快。- 谷歌指南

  • 随着函数大小的增长,内联函数的速度优势趋于减弱。在某些时候,函数调用的开销与函数体的执行相比变得很小,并且失去了好处- 来源

  • 在少数情况下内联函数可能不起作用:

    • 对于返回值的函数;如果存在 return 语句。
    • 对于不返回任何值的函数;如果存在循环、switch 或 goto 语句。
    • 如果函数是递归的。-来源
  • 仅当您指定优化选项时,__inline关键字才会内联函数。如果指定了优化,是否__inline兑现取决于内联优化器选项的设置。默认情况下,只要运行优化器,内联选项就会生效。如果您指定 optimize ,如果您希望__inline关键字被忽略,则还必须指定 noinline 选项。-来源

于 2008-09-28T15:58:15.073 回答