请看标题:C++中的inline函数是什么意思?
这句话只有一个含义:编译器将省略函数的多个定义。
通常情况下,函数不能被定义多次(即如果您将非内联函数定义放入头文件中,然后将其#include到多个编译单元中,您将收到一个链接器错误)。将函数定义标记为“inline”可以抑制此错误(链接器确保发生正确的事情)。
它不代表任何其他意思!
最重要的是,它并不意味着编译器将在每个调用点嵌入已编译的函数。是否会这样做完全取决于编译器的心情,通常内联修饰符对改变编译器的想法几乎没有或根本没有影响。编译器可以(而且确实会)内联未标记为内联的函数,并且可以对标记为内联的函数进行函数调用。
记住省略多个定义就行了。
该函数直接被放在代码中,而不是通过调用,类似于使用宏定义(概念上相似)。
这可以提高速度(无需函数调用),但会导致代码膨胀(如果该函数被使用100次,则现在有100个副本)。
需要注意的是,这并不强制编译器将函数内联,并且如果编译器认为这是一个坏主意,它将忽略你。同样,编译器也可能决定为您将普通函数内联。
这还允许您将整个函数放在头文件中,而不是在cpp文件中实现它(反正您不能这样做,因为如果声明了内联,则会得到无法解析的外部引用,除非当然只有该cpp文件使用它)。
除了关于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
@OldMan
编译器只有在你要求它这样做时才会内联非标记为内联的函数。
只有当“要求”指的是“开启优化”时才会内联。
它只正确地反映了结果而非原因。
在两者方面都是正确的。
内联不会生成链接器可以使用的任何额外信息。编译2个对象文件并检查。正是因为符号未被导出,所以允许多个定义!这不是其目标!
您说的“符号未被导出”是什么意思?内联函数不是静态的。它们的名称是可见的;它们具有外部链接性。引用C++标准:
void h(); inline void h(); // 外部链接 inline void l(); void l(); // 外部链接
多个定义的事情非常重要。这是强制性的:
在使用内联函数的每个翻译单元中定义该内联函数,并且在每种情况下都应完全相同(3.2)。[注意:在翻译单元中出现内联函数的调用可能会在其定义之前遇到。]如果具有外部链接的函数在一个翻译单元中声明为内联函数,则它应在出现的所有翻译单元中声明为内联函数;不需要进行诊断。具有外部链接的内联函数在所有翻译单元中都应具有相同的地址。
内联函数通过可能生成放置在应用程序代码段中的指令来改变应用程序的性能特征。是否内联函数取决于编译器的自由裁量权。根据我的经验,大多数现代编译器都擅长确定何时遵守用户的内联请求。
在许多情况下,内联函数将提高其性能。函数调用本身存在固有开销。然而,内联函数可能会产生负面影响的原因有:
C++ FAQ很好地解释了关键字的复杂性: http://www.parashift.com/c++-faq-lite/inline-functions.html#faq-9.3
非正式地说,这意味着编译器允许将函数的内容嫁接到调用点上,从而消除函数调用。如果您的函数具有大型控制语句(例如if
、switch
等),并且在调用点可以在编译时评估条件(例如在调用点使用常量值),那么您的代码最终会变得更小(未使用的分支会被删除)。
更正式地说,内联函数也具有不同的链接方式。我会让C++专家谈论这个方面。
调用函数会对 CPU 产生一定的性能损失,而不仅仅是线性指令流。CPU 的寄存器必须被写入另一个位置等等。显然,拥有函数的好处通常超过了性能损失。但是,在性能成为问题的情况下,例如著名的“内循环”函数或其他瓶颈,编译器可以将函数的机器代码插入到执行的主要流中,而不是通过 CPU 调用函数。
将函数标记为inline的作用是允许编译器对其进行内联。但并不保证编译器一定会这么做。编译器本身使用复杂的语义来决定是否内联。
当编译器决定一个函数应该被内联时,调用该函数的代码中的调用将被替换为被调用者的代码。这意味着您可以节省堆栈操作、调用本身,并提高代码缓存的局部性。有时这可能会带来巨大的性能提升,特别是在像面向对象代码中使用的访问器这样的1行数据访问函数中。
代价是通常会导致更大的代码,这可能会损害性能。这就是为什么将函数设置为inline只是向编译器发出的“绿灯”,而不需要遵循的原因。编译器将尝试做出最佳决策。
对于不想处理链接特殊性的初学者来说,一个经验法则是:inline函数应该由同一编译单元中的其他函数调用。如果您想实现一个可以在多个编译单元中使用的内联函数,请将其制作成头文件声明和实现的内联函数。
为什么?
例如:在头文件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操作,因此很可能被内联)
但是,如果在头文件中声明了内联函数并将其实现为内联,则可以在包含该头文件的任何编译单元中使用它。 (“代码示例”);
删除内联关键字,编译器将不会引起错误,即使在主函数中调用bar,也不会发生内联,除非您要求编译器内联所有函数。这不是大多数编译器的标准行为。
inline diff operator~ (...)
)。 - puk