参见标题:C ++函数内联是什么意思?
答案 0 :(得分:35)
该函数放在代码中,而不是被调用,类似于使用宏(概念上)
这可以提高速度(没有函数调用),但会导致代码膨胀(如果函数使用了100次,那么现在有100个副本)
你应该注意这并没有强制编译器使函数内联,如果它认为这是一个坏主意它会忽略你。类似地,编译器可能决定为您内联正常函数。
这也允许你将整个函数放在一个头文件中,而不是在一个cpp文件中实现它(你无论如何都不能这样做,因为如果它被声明为内联,你会得到一个未解析的外部,除非当然只有那个cpp文件使用它。)
答案 1 :(得分:26)
这仅仅意味着一件事:编译器将忽略函数的多个定义。
通常不能多次定义函数(即,如果将非内联函数定义放入标题中,然后将#include到多个编译单元中,则会收到链接器错误)。将函数定义标记为“内联”可以抑制此错误(链接器可确保正确的事情发生)。
它并不意味着更多!
最重要的是,它并不意味着编译器会将编译的函数嵌入到每个调用站点中。这是否完全取决于编译器的奇思妙想,并且内联修饰符通常很少或根本没有改变编译器的思想。编译器可以 - 并且确实 - 不标记为内联的内联函数,并且它可以对 标记为内联的函数进行函数调用。
要记住多个定义是值得记住的。
答案 2 :(得分:20)
除了关于inline
的性能影响的其他(完全正确)答案之外,在C ++中你还应该注意到这可以让你安全地将函数放在标题中:
// 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);
这是因为编译器只在第一个目标文件中包含函数的实际主体,需要编译常规可调用函数(通常是因为它的地址已被采用,如上所示)。
如果没有inline
关键字,大多数编译器会给出有关多个定义的错误,例如对于MSVC:
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
答案 3 :(得分:3)
@OldMan
只有在您要求内联函数执行此操作时,编译器才会将内联非标记为内联函数。
只有“请求”是指“开启优化”。
它只对卡斯特的效果是正确的。
两者都是正确的。
内联不会生成链接器可能使用的任何额外信息。 Compiel 2对象文件并检查。它允许多个定义,因为符号不会导出!不是因为那是它的目标!
你是什么意思,“这些符号不会被导出”?内联函数不是静态的。他们的名字是可见的;他们有外部联系。引用C ++标准:
void h(); inline void h(); //外部链接
inline void l(); void l(); //外部链接
多重定义是非常重要的目标。这是强制性的:
内联函数应在每个使用它的翻译单元中定义,并且应具有完全相同的内容 每种情况下都有相同的定义(3.2)。 [注意:在定义之前可能会遇到对内联函数的调用 出现在翻译单元中。 ]如果在一个转换中内联声明了具有外部链接的函数 单位,应在其出现的所有翻译单位内联;无需诊断。一个 具有外部链接的内联函数在所有翻译单元中应具有相同的地址。
答案 4 :(得分:2)
函数体实际上插入了调用函数中。因此,如果您有多次调用此函数,则会获得代码的多个副本。好处是您可以更快地执行。
通常非常短的函数被内联,当函数体的副本不会比为正常函数调用生成的通常的序言/结尾代码大很多时。
您可以在MSDN上有关内联的文章中阅读更多内容 - http://msdn.microsoft.com/en-us/library/z8y1yy88.aspx
答案 5 :(得分:1)
内联函数可能会生成放置在应用程序代码段中的指令,从而改变应用程序的性能配置文件。是否内联函数由编译器决定。根据我的经验,大多数现代编译器都善于确定何时符合用户的内联请求。
在许多情况下,内联函数将改善其性能。函数调用存在固有的开销。然而,有理由说明为什么内联函数可能是负面的:
C ++ FAQ可以很好地解释关键字的复杂性: http://www.parashift.com/c++-faq-lite/inline-functions.html#faq-9.3
答案 6 :(得分:0)
非正式地,这意味着允许编译器将函数的内容移植到调用站点上,这样就没有函数调用。如果您的函数具有大的控制语句(例如,if
,switch
等),并且可以在编译时在调用站点评估条件(例如,在调用站点使用的常量值),然后你的代码会变得更小(未使用的分支被删除)。
更正式地说,内联函数也有不同的联系。我会让C ++专家谈论这个方面。
答案 7 :(得分:0)
调用函数会对CPU产生一定的性能损失,而不仅仅是具有线性指令流。 CPU的寄存器必须写入另一个位置等。显然,具有功能的好处通常超过性能损失。但是,在性能成为问题的地方,例如传说中的“内部循环”函数或其他一些瓶颈,编译器可以将函数的机器代码插入到主要的执行流中,而不是通过CPU的税来调用函数
答案 8 :(得分:0)
编译器允许您标记为内联的函数。无法保证compielr会这样做。编译器本身使用复杂的语义来设置何时执行或不执行。
当编译器决定应该内联函数时,调用者代码中对函数的调用将被calee的代码替换。这意味着您可以节省堆栈操作,调用本身并改善代码缓存的位置。有时这可能会带来巨大的性能提升。特别是在1行数据访问函数中,如面向对象代码中使用的访问器。
成本通常会导致更大的代码,可能会影响性能。这就是为什么将函数设置为内联只是编译器的“绿色标志”,它不需要遵循。编译器将尝试做最好的事情。
作为不想处理连锁特性的初学者的经验法则。内联函数将由相同编译单元中的其他函数调用。如果要实现可在多个编译单元上使用的内联函数,请将其声明为头文件并实现内联函数。
为什么?
示例:at header file inlinetest.h
int foo();
inline int bar();
在编译单元inlinetest.cpp
int foo(){ int r = bar(); return r; }
inline int bar(){ return 5;};
然后在main.cpp
#include "inlinetest.h"
int main()
{
foo();
//bar();
}
一次编译一个目标文件。如果取消注释“bar”调用,则会出错。因为内联函数仅在inlinetest.o对象文件中实现,因此不会导出。同时foo函数,很可能已经嵌入了bar函数的代码(因为bar是单行无I / O操作然后它很可能被内联)
但是如果在头文件中你声明了内联函数并将其实现为内联,那么你就可以在任何包含该头的编译单元中使用它。 (“代码样本”);
删除内联关键字和编译器即使在main处调用bar也不会导致错误除非您要求编译器内联所有函数,否则不会发生内联。这不是大多数编译器的标准行为。