一、什么是inline函数?(what)
inline的本意:把函数调用“展开”成函数体本身,用来减少函数调用的开销。
普通函数
int add(int a, int b) { return a + b; } int x = add(1, 2); // 有一次函数调用inline 函数
inline int add(int a, int b) { return a + b; } int x = add(1, 2);编译器可能会变成类似:
int x = 1 + 2; // 直接展开二、为什么要用 inline?(why)
1、 函数调用本身有开销:压栈、传参、跳转、返回。
函数被频繁调用,不断地有函数入栈,即函数栈,会造成栈空间或栈内存的大量消耗。为了解决这个问题,特别的引入了inline修饰符,表示为内联函数
2、 所以对于很短、调用频繁的函数,inline可以提高效率。
三、inline是“建议”,不是命令
inline int f() { ... }并不保证一定内联!
编译器会自己决定:函数太大 ❌、有递归 ❌、有复杂控制流 ❌
inline只是告诉编译器:我希望你内联
四、inline ≠ 一定快
1、内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率
有时候反而更慢,因为内联会导致代码膨胀、指令缓存压力变大
2、所以inline 适合“短小、频繁”的函数
五、inline 的一个重要作用(容易忽略)
解决头文件重复定义问题(ODR)
// header.h inline int add(int a, int b) { return a + b; }头文件中的这个函数可以被多个
.cpp包含在链接时不会产生“重复定义”错误
这点在头文件函数定义中非常重要(inline在这里更像“链接层面的语义”)
六、类内定义的成员函数,默认就是 inline
class A { public: int f() { return 1; } // 默认 inline };等价于
inline int f() { return 1; }七、总结
1、inline = 希望把函数调用变成代码展开
2、提高小函数性能
3、是建议,不是强制
4、头文件中定义函数常用
5、类内函数默认 inline
补充
对于 五、inline的重要作用中解决头文件重复定义问题(ODR)进行详细说明,
ODR:One Definition Rule
一、要弄清楚这一点,需要先搞清楚为什么会 出现头文件“重复定义”的问题?
举个例子
header.h:
int add(int a, int b) { return a + b; }a.cpp:
#include "header.h"b.cpp:
#include "header.h"当开始编译时 会发现:
a.cpp 编译 → 里面有一个add的函数定义
b.cpp 编译 → 里面也有一个add的函数定义
链接阶段 就会报错,因为链接器看到:“同一个函数add,你给了我两个定义”
这就违反了ODR(One Definition Rule,唯一性定义规则)
于是报错:multiple definition ofadd
二、现在知道什么是头文件“重复定义”的问题了,那inline函数是怎么解决这个头文件重复的问题的?为什么它就可以解决呢?
1、是怎么解决的?
经过改变添加了inline函数过后
header.h:
inline int add(int a, int b) { return a + b; }关键变化(重点)
inline 告诉编译器:这个函数允许在多个 cpp 中出现定义,只要这些定义 一模一样,就不算违反 ODR
也就是说:
a.cpp 里有一个 inline add
b.cpp 里也有一个 inline add
✅ 合法
2、为什么inline可以?
因为内联函数本来就可能被展开:
add(1, 2)可能直接变成
1 + 2那每个.cpp自己“知道”函数内容是合理的
不需要全程序只有一个实体
所以标准就允许:
inline 函数在多个翻译单元中定义
3、和static的区别
static函数(头文件中)
static int add(int a, int b) { return a + b; }每个
.cpp各有一份独立函数,互相完全没关系,不会冲突
inline函数
inline int add(int a, int b) { return a + b; }看起来有多份,逻辑上是同一个函数,链接器允许它们共存
4、总结
头文件中定义函数,要么:inline、static、放进 class 里(默认 inline)
否则就很容易 链接错误
简单来说,inline不只是“优化用”,它还告诉链接器:这个函数允许在多个 cpp 中定义,不算重复。