目录
1. 概念
以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数压栈的开销,内联函数提升程序运行的效率。
以Add函数为例:
int Add(int x, int y) { int z = x + y; return z; }
上述Add函数如若频繁调用,则效率上会存在一定的损失。假设我调用10次,那么就要建立十次栈帧,建立栈帧又要保存寄存器,压参数等等一系列操作都会造成效率损失。
- 在先前学习的C语言中,就给出了解决方案:宏
#define ADD(x, y) ((x) + (y))
- 既然用宏就可以解决,C++为何引出inline?
使用宏确实可以帮助我们避免调用栈帧继而不会造成效率损失,但是宏的写法上欠妥,我们需要注意结尾不能加分号,要注意优先级带来的问题而频繁加括号……C++为了填补宏书写规则麻烦的坑,专门出了一个更为方便书写的,也就是今个要谈论的内联函数(inline)。综上,结论如下:
- 解决宏函数晦涩难懂,容易写错的问题;
- 解决宏不支持调试,不支持类型安全的检查等问题。
- 内联函数(inline)如何使用?
只需要在函数前面加上inline即可:
inline int Add(int x, int y) { int z = x + y; return z; }
此时我们再调用函数就不会再建立栈帧了,函数直接会在调用的地方展开。
- 内联函数(inline)如何支持调试的呢?
需要查看其反汇编,查看方式:
- 在release模式下,查看编译器生成的汇编代码中是否存在call Add;
- 在debug模式下,需要对编译器进行设置,否则不会展开(因为debug模式下,编译器默认不会对代码进行优化,以下给出vs2013的设置方式);
仔细看,此时每调用一次Add函数,均不会有call指令了, 此时再和我们没有加inline调用栈帧的版本对比下:
- 综上:inline的好处如下:
- debug支持调试;
- 不易写错,就是普通函数的写法。
2.特性
- inline是一种以空间换时间的做法,省去调用函数额开销。所以代码很长或者有循环/递归的函数不适宜使用作为内联函数。
- inline对于编译器而言只是一个建议,编译器会自动优化,如果定义为inline的函数体内有循环/递归等等,编译器优化时会忽略掉内联。
- inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。
- 1、解释特性1和2:
假设我们现在有一个10行的函数,如若该函数有1000个调用的地方,如果inline替换,则是1000*10条指令,如果不替换,则是1000+10条指令,二者相差10倍之大,由此可见,代码很长或调用次数过多或存在循环/递归不适宜用内联函数
代码演示:
- 代码演示:
此段代码还能够证明inline对于编译器只是一个建议 。
- 解释特性3:inline不建议声明和定义分离
// F.h文件 #include <iostream> using namespace std; inline void f(int i); // F.cpp文件 #include "F.h" void f(int i) { cout << i << endl; } // test.cpp文件 #include "F.h" int main() { f(10); return 0; }
此段代码中,inline函数的声明和定义分离,此时我们编译没有错误,可是在链接时发生错误:
原因如下:
F.h声明了我是一个内联函数,F.h文件会在F.cpp文件展开,要注意,inline函数在编译过程中不会生成地址,其符号表里没有地址,F.cpp文件中调用时找不到该函数地址。所以inline不建议声明和定义分离。
解决方法如下:
将声明和定义都放到F.h文件,就没有问题了,总之就是声明定义不能分离。
3. 经典面试题
- 问题1:宏的优缺点?
优点:
- 增强代码的复用性;
- 提高性能。
缺点:
- 不方便调试宏(因为预编译阶段进行了替换);
- 导致代码可读性差,可维护性差,容易误用;
- 没有类型安全的检查。
- 问题2:C++有哪些技术代替宏?
- 常量定义,换用const;
- 函数定义,换用内联函数。