函数的好处
- 降低代码冗余,提高代码可读性
- 使用函数可以确保行为的统一,每次相关操作都能按照同样的方式进行
- 只需修改函数就可以改变所有调用该函数的操作,而不需要先找到所有等价表达式出现的地方,再逐一修改。
- 函数可以被其他应用重复利用,省去了程序员重新编写的代价
为什么需要内联函数
虽然函数有很多优点,但是也有一个潜在的缺点,对于逻辑简单且代码量少的函数,其函数的调用比求等价表达式的值要慢一些。
在大多数机器上,一次函数调用包含一系列工作:
- 调用前要先保存寄存器,并在返回时恢复;
- 可能需要拷贝实参
- 程序转向一个新的位置继续执行
而内联函数的优点就是可以避免函数调用的开销,如果一个函数是内联的,那么在编译时
,编译器会把该函数的代码副本放置在每个调用该函数的地方。
int add(int a, int b) {
return a + b;
}
int main()
{
int a = 10, b = 20;
int sum = add(a, b);
return 0;
}
通过对代码进行调试和反汇编,我们发现主函数通过call来对函数进行调用
inline int add(int a, int b) {
return a + b;
}
//主函数同上
我们将add函数改成内联函数,我们发现内联函数的调用表达式用内联函数的函数体进行了替换,这其实就是空间换时间的写法
不仅如此我们还发现如果我们在内联函数内部设置断点,发现在调试时并不会进入该函数中。
inline内联说明只是向编译器发出的一个请求,由于编译器有自己的优化策略,可能导致我们的inline不起作用,所以这里为了调试,对于visual studio编译器,按照上图方式改变内联函数的扩展方式
内联函数的定义
- 在函数名前加上关键字inline
- 对于类中定义的函数,即使没有inline光健字,其也是内联函数
对内联函数进行任何修改,都需要重新编译程序,因为编译器需要重新更换一次所有的代码,否则将会继续使用旧的函数。
内联函数的使用
注意事项
:
-
如果函数体内的代码比较长,使用内联将导致内存消耗代价较高
-
如果函数体内出现循环,switch语句,那么执行函数体内代码的时间要比函数调用的开销大
-
对于虚函数或者递归函数,inline常常不起作用;
原因很简单,内联函数是在编译阶段进行展开,对于虚函数,C++的多态性就是通过虚函数实现的,子类可以重写父类的虚函数,而具体是使用基类的还是某个子类的虚函数,需要程序运行时才能确定;对于递归函数也一样,递归的层数往往需要程序运行时才能确定
什么情况下使用内联函数
- 当函数体代码量比较下,通常在10行以内
- 函数体内没有循环、swith语句
- 不是虚函数和递归函数
内联函数,宏定义和普通函数的区别
- 宏定义不是函数,但是使用起来像函数;内联函数本质上是一个函数,内联函数一般用于函数体代码比较简单的函数,不能包含复杂的控制语句,while、switch,并且内联函数本身不能直接调用自身。如果内联函数函数体过于复杂,编译器将自动把内联函数当成普通函数来执行
- 宏定义在预编译阶段,把所有宏名用宏体进行替换,即字符串替换,而内联函数是在编译阶段进行代码插入,编译器会在调用内联函数的地方直接把内联函数的内容展开,这样可以省去函数调用的压栈出栈的开销,提高效率。
- 宏定义是没有类型安全检查和自动类型转换的,无论对还是错都是直接替换;内联函数会进行类型安全检查和自动类型转换,如果正确,函数体会直接替换函数调用语句。
普通函数在被调用的时候,系统首先要到函数的入口地址去执行函数体,执行完成之后再回到函数调用的地方继续执行,函数始终只有一个复制。 内联函数不需要寻址, 当调用到内联函数时,将内联函数在调用点。
参考
《C++ Primer 第五版》
C++内联函数的使用