在掌握类的基本声明(如之前介绍的 class 结构)后,我们需要关注函数的执行效率。故本篇解决的核心问题就是:如何在C++中让”小而频繁”的函数调用变得更快?答案就是内联函数

类体内定义与inline自动生成

在C++中,函数的定义位置直接决定了它是否被编译器视为内联候选。在类的声明内部直接给出函数体(带有大括号 {})的函数,编译器会 自动将其视为内联函数的候选,即使没有显式写出 inline 关键字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class MathUtils {
public:
// 类体内定义 → 自动内联候选
int square(int x) { return x * x; }

// 显式声明 inline,效果相同
inline int cube(int x) { return x * x * x; }

// 非内联:仅有声明,定义在类外
int factorial(int n);
};

// 类外定义:需要显式声明,加上inline
inline int MathUtils::factorial(int n) {
return (n <= 1) ? 1 : n * factorial(n - 1);
}

内联的本质是用空间换时间:编译器将函数调用处直接替换为函数体代码,避免函数调用的开销(参数压栈、跳转、返回)。但这只是一个 建议,编译器有权忽略。如果函数体很大或包含复杂控制流,编译器通常会拒绝内联,因为代码膨胀反而会降低性能。

内联的实质、与宏的区别、编译器决定权

内联函数并非简单的文本替换,它与C语言中的宏有本质区别。理解这些区别,才能安全地使用内联。

image

宏的典型陷阱

1
2
3
4
5
6
#define SQUARE(x) ((x)*(x))
// 调用:SQUARE(++a) → 展开为 ((++a)*(++a)) → 未定义行为!

// 内联版本安全:
inline int square(int x) { return x * x; }
// 调用:square(++a) → 只递增一次,行为明确

编译器的最终决定权

即使函数体在类内定义,编译器也可能拒绝内联。常见拒绝原因:函数体过大、包含递归、包含循环、包含虚函数或复杂控制流。编译器通常会发出警告(如 -Winline)。因此,inline 只是建议,不是命令。