C ++函数内联意味着什么?

时间:2020-03-06 14:57:47  来源:igfitidea点击:

参见标题:C ++函数内联意味着什么?

解决方案

函数体实际上是插入在调用程序函数内部的。因此,如果多次调用此函数,则将获得该代码的多个副本。好处是我们可以更快地执行。

当函数主体的副本不会比为正常函数调用生成的常规序言/结尾代码大很多时,通常会内联非常短的函数。

我们可以在MSDN文章中了解有关内联http://msdn.microsoft.com/zh-cn/library/z8y1yy88.aspx的更多信息

非正式地,这意味着允许编译器将函数的内容移植到调用站点上,从而不进行函数调用。如果函数具有较大的控制语句(例如," if"," switch"等),并且条件可以在编译时在调用站点进行评估(例如,在调用站点使用的常量值),那么代码将结束小得多(未使用的分支被丢弃)。

更正式地说,内联函数也具有不同的链接。我将让C ++专家讨论这一方面。

该函数放置在代码中,而不是被调用,类似于在概念上使用宏

这可以提高速度(不调用函数),但会导致代码膨胀(如果该函数使用了100次,则现在有100个副本)

我们应该注意,这不会强制编译器使函数内联,并且如果认为这是一个不好的主意,它将忽略我们。同样,编译器可能会决定为我们内联常规函数。

这也使我们可以将整个函数放在头文件中,而不是在cpp文件中实现(无论如何都不能实现,因为如果声明为内联,则将得到一个未解析的外部文件,当然,除非只有那个cpp文件使用了它)。

内联函数可能通过生成放置在应用程序代码段中的指令来更改应用程序的性能配置文件。是否内联函数由编译器决定。以我的经验,大多数现代编译器都擅长确定何时满足用户的内联请求。

在许多情况下,内联函数可以提高其性能。函数调用存在固有的开销。但是,为什么有一个内联函数可能是负数是有原因的:

  • 通过复制代码来增加二进制可执行文件的大小可能会导致磁盘抖动,从而降低应用程序的运行速度。
  • 内联代码可能会导致缓存未命中,或者可能会导致缓存命中,具体取决于体系结构。

C ++常见问题解答很好地解释了关键字的复杂性:

http://www.parashift.com/c++-faq-lite/inline-functions.html#faq-9.3

调用函数会给CPU带来一定的性能损失,而不仅仅是线性的指令流。必须将CPU的寄存器写入其他位置,等等。显然,具有功能的好处通常胜过性能损失。但是,在性能会成为问题的情况下,例如寓言中的"内部循环"功能或者其他瓶颈,编译器可以将函数的机器代码插入执行的主要流中,而无需花费CPU的税来调用函数。

// my_thing.h
inline int do_my_thing(int a, int b) { return a + b; }

// use_my_thing.cpp
#include "my_thing.h"
...
    set_do_thing(&do_my_thing);

// use_my_thing_again.cpp
...
    set_other_do_thing(&do_my_thing);

关于"内联"对性能的影响,还有其他(完全正确)的答案,在C ++中,我们还应该注意,这可以让我们安全地将函数放在标头中:

这是因为编译器仅在第一个目标文件中包含该函数的实际主体,而该主体文件需要常规的可调用函数进行编译(通常是因为如上所述,它的地址已被占用)。

use_my_thing_again.obj : error LNK2005: "int __cdecl do_my_thing(int,int)" (?do_my_thing@@YAHHH@Z) already defined in use_my_thing.obj
<...>\Scratch.exe : fatal error LNK1169: one or more multiply defined symbols found

没有inline关键字,大多数编译器会给出关于多重定义的错误,例如对于MSVC:

标记为内联的函数允许编译器内联。不能保证compielr会做到。编译器本身使用复杂的语义来表示何时执行此操作。

当编译器决定应内联一个函数时,调用程序代码中对该函数的调用将替换为calee的代码。这意味着我们可以节省堆栈操作,调用本身并提高代码缓存的局部性。有时这可能会导致巨大的性能提升。特别是1行数据访问功能,例如面向对象代码中使用的访问器。

代价是通常会导致更大的代码,这可能会损害性能。这就是为什么将函数设置为内联只是编译器的"绿色标志",而无需遵循。编译器将尝试做最好的事情。

对于不想处理链接特性的初学者来说,这是一个经验法则。内联函数将由同一编译单元中的其他函数调用。如果要实现可在多个编译单元上使用的内联函数,请使其成为声明并实现内联函数的头文件。

为什么?

int foo();
inline int bar();

示例:在头文件inlinetest.h中

int foo(){ int r = bar(); return r; }

 inline int bar(){ return 5;};

在编译单元inlinetest.cpp

#include "inlinetest.h"
 int main()
 {
  foo();
 //bar();
  }

然后在main.cpp

一次编译一个目标文件。如果取消注释该"栏"调用,则会出现错误。因为内联函数仅在inlinetest.o对象文件上实现,所以不会导出。同时foo函数很可能已经在其上嵌入了bar函数的代码(因为bar是单行,没有I / O操作,所以很可能会内联)

但是,如果我们在头文件中声明了内联函数并内联实现了该函数,则可以在包含该头的任何编译单元中使用它。
("代码示例");

删除inline关键字,即使在main处进行bar调用,编译器也不会引起错误。除非我们要求编译器对所有函数进行内联,否则不会发生内联。在大多数编译器上,这不是标准行为。

这仅意味着一件事和一件事:编译器将忽略该函数的多个定义。

通常,不能多次定义一个函数(即,如果将非内联函数定义放在标头中,然后将其#include到多个编译单元中,则会收到链接器错误)。将函数定义标记为"内联"可消除此错误(链接器确保发生正确的事情)。

它没有更多的意义了!

最重要的是,这并不意味着编译器会将已编译的函数嵌入到每个调用站点中。是否发生这种情况完全取决于编译器的想法,通常inline修饰符几乎不会改变编译器的想法。编译器可以并且确实可以执行未标记为内联的内联函数,并且可以对标记为内联的函数进行函数调用。

要记住多个定义。

The compilers only inline non marked as inline functions ONLY if you request it to do so.

@老人

Its correct only on the effcts nto the casuse.

仅当"请求"是指"打开优化"时。

Inline do not generate any extra info that the linker may use. Compiel 2 object files and check. It allow multiple definitions exaclty because the symbols are not exported! Not because that is its goal!

两者都是正确的。

void h();
  inline void h(); // external linkage
  
  inline void l();
  void l(); // external linkage

我们是什么意思,"不导出符号"?内联函数不是静态的。他们的名字是可见的。他们有外部联系。引用C ++标准:

An inline function shall be defined in every translation unit in which it is used and shall have exactly the
  same definition in every case (3.2). [Note: a call to the inline function may be encountered before its definition
  appears in the translation unit. ] If a function with external linkage is declared inline in one translation
  unit, it shall be declared inline in all translation units in which it appears; no diagnostic is required. An
  inline function with external linkage shall have the same address in all translation units.

段落数量不匹配