• 文章
  • C++ 内联函数是什么
发布
2014 年 5 月 6 日 (最后更新:2014 年 5 月 6 日)

C++ 内联函数是什么

评分:3.9/5 (5140 票)
*****
在 C 语言中,我们使用了宏函数,这是一种编译器使用的优化技术,用于减少执行时间等。那么问题来了,C++ 有什么类似的技术,而且是以什么更好的方式呢?内联函数应运而生,这是一种编译器使用的优化技术,尤其用于减少执行时间。我们将涵盖内联函数的“是什么、为什么、何时及如何”。

什么是内联函数
内联函数是 C++ 的一项增强功能,用于提高程序的执行时间。可以指示编译器将函数设为内联,以便编译器可以在调用这些函数定义的地方替换它们。编译器在编译时替换内联函数的定义,而不是在运行时引用函数定义。
注意:这只是对编译器的一个建议,要求将其设为内联。如果函数很大(就可执行指令而言),编译器可能会忽略“inline”请求,并将该函数视为普通函数。

如何使函数内联
要使任何函数成为内联函数,请在其定义前加上关键字“inline”。

示例 –
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Class A
{
 Public:
    inline int add(int a, int b)
    {
       return (a + b);
    };
}

Class A
{
 Public:
    int add(int a, int b);
};

inline int A::add(int a, int b)
{
   return (a + b);
}


为什么使用 –
在许多地方,我们会为小的任务/功能创建函数,这些函数包含简单且数量少的执行指令。想象一下它们每次被调用时产生的调用开销。
遇到普通函数调用指令时,程序会保存函数调用语句后面指令的内存地址,将要调用的函数加载到内存中,复制参数值,跳转到被调用函数的内存位置,执行函数代码,存储函数的返回值,然后跳转回执行被调用函数之前保存的指令地址。这会产生很大的运行时开销。
C++ 内联函数提供了一种替代方案。使用 inline 关键字后,编译器会将函数调用语句替换为函数代码本身(称为展开过程),然后编译整个代码。因此,有了内联函数,编译器就不必跳转到另一个位置来执行函数,然后再跳回来,因为被调用函数的代码已提供给调用程序。
通过以下优点、缺点和性能分析,您将能够理解“为什么”使用 inline 关键字
优点 :-
1. 通过避免函数调用开销来加速程序。
2. 在发生函数调用时,节省了在堆栈上压入/弹出变量的开销。
3. 节省了从函数返回调用的开销。
4. 通过利用指令缓存来提高局部性。
5. 通过标记为内联,可以将函数定义放在头文件中(即可以包含在多个编译单元中,而不会导致链接器报错)

缺点:-
1. 由于代码展开,增加了可执行文件的大小。
2. C++ 内联在编译时解析。这意味着如果您更改了内联函数中的代码,您将需要重新编译所有使用它的代码,以确保它已更新。
3. 在头文件中使用时,它会使您的头文件变大,其中包含用户不关心的信息。
4. 如上所述,它会增加可执行文件的大小,这可能会导致内存抖动。更多的页面错误会降低程序的性能。
5. 有时可能不适用,例如在嵌入式系统中,由于内存限制,不希望有大的可执行文件。

何时使用 -
可以根据程序员的需求将函数设为内联。下面是一些有用的建议:
1. 当需要性能时,使用内联函数。
2. 使用内联函数而不是宏。
3. 倾向于在类外部使用内联关键字以及函数定义,以隐藏实现细节。

关键点 -
1. 这只是一个建议,并非强制。编译器可能会或可能不会内联您标记为内联的函数。它也可能决定在编译或链接时内联未标记为内联的函数。
2. 内联函数的工作方式类似于由编译器控制的复制/粘贴,这与预处理器宏有很大不同:宏会被强制内联,会污染所有命名空间和代码,并且不容易调试。
3. 类内部声明和定义的所有成员函数默认都是内联的。因此,无需显式定义。
4. 虚函数不应被内联。不过,有时当编译器能确定对象类型时(例如,对象是在同一个函数体内部声明和构造的),即使是虚函数也会被内联,因为编译器确切地知道对象的类型。
5. 模板方法/函数并不总是被内联(它们出现在头文件中不会使它们自动内联)。
6. 大多数编译器会对递归函数进行内联,但有些编译器提供了 #pragmas -
Microsoft C++ 编译器 - inline_recursion(on),并且可以使用 inline_depth 控制其限制。
在 GCC 中,您还可以通过命令行参数 --max-inline-insns-recursive 来传递此选项。