C++基础整理(7)之关键字 inline(内联函数)
注:整理一些突然学到的C++知识,随时mark一下
例如:忘记的关键字用法,新关键字,新数据结构
C++ 的 inline/内联函数
提示:本文为 C++ 中 inline 的用法和举例
一、inline关键字(内联函数)
在C++中,inline是一个用于函数定义的修饰符,格式是直接在函数返回类型的最前面加上这个inline,产生效果是建议编译器将整个函数体代码段插入到每个调用点然后展开,从而消除函数调用的开销(代码转移到函数对应的内存地址执行,会记忆转移前的地址,反复调用就会转移多次),inline直接将函数嵌入到函数开始调用的那块。
inline return_type function_name(parameter list) {
// 函数体
}
使用inline关键字的主要目的是为了提高函数的执行效率。当函数体较小且被频繁调用时,内联函数通常能带来性能上的提升。这是因为内联函数可以避免函数调用的开销,包括参数传递、堆栈的创建和销毁等。然而,需要注意的是,inline是一个对编译器的建议,编译器可以选择忽略这个建议,特别是当长且复杂(如大量循环等)的内联函数会导致嵌入后的代码膨胀反而影响性能。
1、inline使用举例
下面是内联函数的示例:
inline int add(int a, int b) {
return a + b;
}
在上面的例子中,add函数被声明为内联函数。当编译器遇到add函数的调用时,它可能会尝试将函数的代码直接插入到调用点,而不是进行常规的函数调用。
2、类成员函数的内联
类的成员函数可以被inline。可以定义成内联函数。
对于在类体内部定义的成员函数,其实默认就是inline的,此时成员函数一般都省略写inline关键字。然而,是否内联还是取决于编译器,编译器可能会根据函数的大小和其他因素来决定是否将函数内联。
但应当特别留意的是,若成员函数在类体外部定义而非内部定义,那么系统并不会默认将其视为内联(inline)函数。这意味着在调用这些成员函数时,会遵循与普通函数调用相同的机制,包括可能的压栈和跳转开销。如果希望将这些成员函数指定为内联函数,以期望获得性能上的优化,则必须显式地在函数声明前使用inline关键字进行声明。这样做可以指导编译器在可能的情况下将函数体直接插入到调用点,从而消除函数调用的开销,提高程序执行的效率。
下面给一个外部定义类成员函数内联的例子:
// MyClass.h 文件里
class MyClass {
public:
MyClass(int value);
inline int getValue() const; // 成员函数在类内声明为inline,也可以不加inline,默认就是
private:
int m_value;
};
// MyClass.cpp 文件里
#include "MyClass.h"
// MyClass的构造函数
MyClass::MyClass(int value) : m_value(value) {}
// getValue成员函数在类外定义为inline
inline int MyClass::getValue() const { // 注意这里也使用了inline关键字
return m_value;
}
3、inline缺陷
尽管内联函数可以提高性能,但它们也有一些缺点和限制:
代码膨胀:如果内联函数体很大,或者函数被频繁调用,那么将函数体插入到每个调用点可能会导致代码体积显著增加,从而可能降低指令缓存的效率,反而影响性能。
编译时开销:内联函数可能导致编译时间增加,因为编译器需要处理更多的代码。
链接时问题:内联函数通常需要在头文件中定义,而不是在源文件中。这可能会导致多个定义的问题,除非正确地使用inline关键字和链接器选项来避免这些问题。
编译器优化:编译器并不一定会内联所有标记为inline的函数。是否内联一个函数取决于多种因素,包括函数的大小、调用频率以及编译器的优化设置等。
因此,在决定是否使用内联函数时,需要权衡其带来的性能提升与可能带来的代码膨胀和编译时开销。在大多数情况下,最好让编译器自动决定哪些函数应该内联,而不是显式地使用inline关键字。
4、 内联(inline)与宏定义(define)的区别比较
内联函数与宏定义在C++编程中都有展开代码来提高代码执行效率的作用,它们区别如下:
(1)首先,从定义和性质上看,宏定义并非真正的函数,它在预处理阶段进行文本替换,即用宏体替换所有的宏名。而内联函数本质则仍然是一种函数,它在编译时直接嵌入到目标代码中,替换了函数调用,从而消除了函数调用的开销。内联函数具有普通函数所有的特性,比如有返回值、参数列表等,可以进行类型安全检查,而宏定义则没有这些特性。
(2)从使用方式和调试角度看,宏定义在定义时需要小心处理宏参数,以避免出现二义性,而内联函数则不存在这个问题。此外,由于内联函数是函数,因此它可以进行调试,而宏定义则不能。
(3)从作用范围上看,内联函数作为类的成员函数时,可以访问类的所有成员(公有、保护、私有),而宏定义则不能。
(4)在代码展开方面,虽然宏定义和内联函数都实现了代码的直接插入,但它们的处理时机不同。宏定义在预处理阶段就完成了所有的替换工作,而内联函数则是在编译阶段进行插入。这样的差异使得内联函数在效率提升的同时,还能确保代码的安全性和可读性。通过避免函数调用的压栈和清栈开销,内联函数进一步提高了程序的执行效率