inline内联函数的使用
又是好久没有更新了,家人们,主播最近写完了c++的几个轮子项目,工程能力有了一内内的提高,现在在准备八股,希望暑假or大三能找到一个满意的实习吧(虽然就目前来看,c++全是劝退,我真服了,后悔没有选java了,后面主播慢慢转go吧)
在C++中,inline
关键字主要用于定义内联函数,具有以下两个主要作用:
优化性能:
使用inline
修饰的函数,建议编译器在调用该函数时,将函数体的代码直接插入到调用点。相比正常的函数调用过程:寄存器保存,参数压栈,跳转到函数地址,执行函数体,返回并恢复寄存器
这可以减少函数调用的开销,尤其适用于小型、频繁调用的函数。例如:
1
inline int square(int x) { return x * x; }
当调用
square(5)
时,编译器可能将代码替换为5 * 5
,避免函数调用开销。
注意:inline
只是建议,编译器可能忽略它,尤其在以下情况下:- 函数体过大
- 函数包含循环、递归、静态变量、switch 或 goto 语句
- 函数地址被取用(如通过函数指针)
允许多个定义:
inline
函数可以在头文件中定义,并被多个编译单元(.cpp 文件)包含,而不会导致链接错误(违反“单一定义规则”)。这是因为inline
函数允许多个相同的定义,链接器会选择一个定义并丢弃其他重复定义。
示例:1
2// example.h
inline int add(int a, int b) { return a + b; }多个 .cpp 文件包含此头文件时,不会引发重定义错误。
看到第二个作用,主播不禁想到了自己在项目开发的过程中,hpp文件那是嘎嘎重复被cpp文件使用啊(虽然有#program once),而头文件中的类中难免有一些成员函数是直接定义在类里面的,那为什么没有事呢。
(正常情况下,函数只能在头文件中声明,而不能在头文件中定义)
1. 类内函数的内联特性
隐式内联:
在类定义中直接定义的成员函数(包括普通成员函数、构造函数、析构函数等)默认是
inline
函数,无需显式使用inline
关键字。这是 C++ 标准规定的行为,原因是为了方便在头文件中定义类时,允许函数体直接嵌入类定义,而不会违反单一定义规则(ODR)。
示例:
1
2
3
4
5
6
7// myclass.h
class MyClass {
public:
int getValue() { return value; } // 隐式内联
private:
int value = 0;
};getValue
的定义在类体内,自动视为inline
,可以在头文件中安全使用,多个.cpp
文件包含时不会导致多重定义错误。
**显式
inline
**:如果成员函数在类内仅声明,在类外定义,则需要显式使用
inline
关键字来指定内联行为,否则它将是普通函数(非内联)。示例:
1
2
3
4
5
6
7
8
9// myclass.h
class MyClass {
public:
int getValue(); // 仅声明
private:
int value = 0;
};
inline int MyClass::getValue() { return value; } // 显式内联- 在类外定义的
getValue
使用inline
,确保它可以在头文件中定义,且允许多个翻译单元使用。
- 在类外定义的
效果:
- 无论是隐式还是显式内联,编译器可能将函数体直接插入调用点,减少调用开销。
- 链接器会处理多份定义,确保符合 ODR。
2. 类内 inline
函数的实际作用
类内 inline
函数的作用与普通 inline
函数一致,主要包括:
性能优化
:
适合小型、频繁调用的成员函数(如 getter/setter、状态检查)。
在嵌入式系统中,内联可以减少函数调用开销,优化实时性能。例如,在 FreeRTOS 中,检查任务状态的函数可以定义为内联:
1
2
3
4
5
6class TaskManager {
public:
bool isTaskRunning() { return taskState == RUNNING; } // 隐式内联
private:
TaskState taskState;
};
允许多定义
:
类定义通常放在头文件中,隐式或显式内联的成员函数允许在多个
.cpp
文件中包含头文件,而不会导致链接错误。示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// task.h
class Task {
public:
void incrementCounter() { counter++; } // 隐式内联
private:
int counter = 0;
};
// main.cpp
void setup() { Task t; t.incrementCounter(); }
// other.cpp
void loop() { Task t; t.incrementCounter(); }incrementCounter
是隐式内联,多个源文件包含task.h
不会报错。
接下来是inline函数的使用注意事项
- 不能存在任何形式的循环语句
- 不能存在过多的条件判断语句
- 函数体不能过于庞大
- 内联函数声明必须存在于调用语句之前