内联函数是否生效由编译器决定,需将定义置于头文件;仅对短小频繁调用函数考虑,避免含循环、递归等;确认内联应查汇编而非调试;inline函数比宏更安全,模板和类内定义成员函数默认内联。

内联函数不是加个 inline 就快了
加 inline 关键字只是向编译器“建议”内联,实际是否展开由编译器决定——尤其在开启 -O2 或更高优化等级后,编译器会自己判断哪些函数值得内联,甚至把没标 inline 的小函数也内联掉。手动标记反而可能干扰优化策略。
常见错误现象:inline 函数定义放在 .cpp 里,导致链接时报 undefined reference to 'xxx'——因为内联函数必须在每个使用它的编译单元里可见,所以定义得放在头文件中。
- 只对短小、频繁调用(比如访问器、简单计算)的函数考虑
inline - 避免在函数体内有循环、递归、
static变量或异常处理——这些会让编译器大概率拒绝内联 - 调试时(
-O0)内联通常被禁用,别靠单步调试验证是否真内联了
怎么确认函数真的被内联了
不能靠肉眼猜,得看编译器输出的汇编。用 g++ -S -O2 foo.cpp 生成 foo.s,搜索调用点:如果没看到 call 指令,而是直接看到函数体内的指令序列(比如几条 mov、add),基本就是内联成功了。
更轻量的方法是加编译器提示:GCC/Clang 支持 __attribute__((always_inline)),但仅用于强需求场景(比如性能关键路径+已确认未内联),滥用会导致代码膨胀,cache miss 上升反降速。
立即学习“C++免费学习笔记(深入)”;
-
__attribute__((noinline))可强制不内联,适合做性能对比基线 - 注意
inline+virtual几乎无效——虚函数调用走 vtable,编译期无法确定目标 - 模板函数默认隐式
inline,不用额外写,但定义仍需在头文件
内联和宏的区别不只是语法
宏是文本替换,不检查类型、不进作用域、可能重复求值;inline 函数是真正的函数,有类型安全、作用域隔离、参数只求值一次。
典型踩坑:#define SQUARE(x) x * x 遇到 SQUARE(a++) 就出问题;而 inline int square(int x) { return x * x; } 安全得多。
- 优先用
inline替代功能简单的宏,尤其是带参数的 - 宏适合编译期常量计算(如
sizeof表达式)、条件编译、或需要操作预处理器符号的场景 - 不要为了“看起来像宏”而在
inline函数里用__LINE__这类预处理器宏——它展开的是定义处的行号,不是调用处
成员函数自动内联的边界在哪
类内部定义的成员函数(包括构造函数、operator== 等)默认是 inline 的,哪怕没写关键字。但这个“内部定义”指函数体直接写在类定义里,而不是声明在类里、定义在类外。
容易忽略的点:如果类定义在头文件 A.h,你又在 B.cpp 里 #include A.h 并调用该成员函数,没问题;但如果在 C.h 里又 #include A.h,且多个 .cpp 都包含 C.h,依然不会链接冲突——因为 inline 函数允许多重定义。
- 类外定义的成员函数想内联,必须显式加
inline关键字,并确保定义在头文件中 - 析构函数内联要小心:如果类有虚函数,析构函数通常不能内联(除非编译器能证明对象类型完全可知)
- 模板类的成员函数天然 inline,但若模板实例化在多个 TU 中出现,仍依赖 ODR(One Definition Rule)保证一致性
真正影响性能的往往不是某一个函数是否内联,而是数据局部性、分支预测失败、缓存行对齐这些底层因素。把 inline 当银弹去套,十有八九会绕远路。











