内联函数
在函数运行的时候都有一些额外的开销,比如a函数调用b函数,在调用b函数的时候先要把a函数暂停,给b函数分配一块内存空间,再进行参数的传递,把实际参数的值拷贝给形式参数,接下去再执行b函数,函数执行结束以后,将b函数的空间收回,最后把b函数中的返回值返回到a函数,然后a函数再继续执行,所以函数调用过程中有这些额外的代价。如果一个函数很复杂,函数本身就要运行很长时间,这些额外的代价可以忽略,分配空间和参数传递是可以忽略的时间;但是如果这个函数本身就很简单,执行的时间挺短,那函数调用产生额外的代价就非常的可观,所以函数调用也有不好的地方,浪费时间,执行效率低。但是如果不写成函数的形式,对程序的可读性有一定影响,所以C++提出了内联函数,可以有函数的形式,让函数保持模块化可读性良好的特性,但是又没有函数调用的代价。
特点:
- 有函数的形式,但无函数调用的代价
- 编译时用函数体代码替代函数调用
定义
inline 返回类型 函数名(形式参数列表)
指定内联函数,只需要在函数定义处增加 inline 关键字
函数定义必须出现在函数调用之前
实例
#include<iostream>
inline double square(double x) {return x*x;}
int main()
{
using namespace std;
double a,b;
double c = 13.0;
a = square(5.0);
b = square(4.5 + 7.5);
cout<< "a =" << a << ", b = " << b << "\n";
cout<< "c =" << c << ", c squared = " << square(c++) << "\n";
cout<< "Now c = " << c << "\n";
return 0;
}
运行结果:
a = 25, b = 144
c = 13, c squared = 169
Now c = 14
内联函数与宏定义
- inline是函数,宏不是函数。
- 内联函数可以调试,进行诸如类型安全检查、语句是否正确等编译功能;宏不具有这样的调试功能;
- 宏在定义时要小心处理宏参数,一般用括号括起来,否则容易出现二义性。而内联函数不会出现二义性。
- 内联函数是在编译时展开,而宏在预编译时展开;在编译的时候,内联函数直接被嵌入到目标代码中去,而宏只是一个简单的文本替换。
使用宏代码最大的缺点是容易出错,预处理在拷贝宏代码时常常产生意向不到的边际效应。
例如:
#define MAX(a,b) (a)>(b)?(a):(b)
语句:
result = MAX(i,j)+2;
//被预处理器扩展为
result = (i)>(j)?(i):(j)+2;
由于运算符"+“比运算符”?:"的优先级高,所以上述语句并不等价于
result = ((i)>(j)?(i):(j))+2;
如果把宏代码改写为:
#define MAX(a,b) ((a)>(b)?(a):(b))
则能解决优先级的问题。但是会引发另一个问题
result = MAX(i++,j);
//被预处理器扩展为
result = (i++)>(j)?(i++):(j) //在同一个表达式中i被两次求值。
内联机制既具备宏代码的效率,又增加了安全性,而且可以自由操作类的数据成员,所以应该尽量使用内联函数来取代宏代码。
何时使用内联函数
内联能提高函数的执行效率,那么为什么不把所有的函数都定义成内联函数呢?
内联不是万灵丹,它以代码膨胀(拷贝)为代价,仅仅省去了函数调用的开销,从而提高程序的执行效率。(开销指的是参数的压栈、跳转、退栈和返回操作)。
一方面,如果执行函数体内代码的时间比函数调用的开销大得多,那么inline效率收益会很小。
另一方面,每一处内联函数的调用都要拷贝代码,使程序的总代码量增大,消耗更多的内存空间。
以下情况不宜使用内联:
如果函数体内代码比较长,使用内联函数将导致可执行代码膨胀过大。
如果函数体内出现循环或者其他复杂的控制结构,那么执行函数体内代码的时间将比函数调用的开销大得多。
总结:当函数比较复杂时,函数调用的时空开销可以忽略,大部分的 CPU 时间都会花费在执行函数体代码上,这时使用内联效率很低,而且会导致代码膨胀过大。所以我们一般是将非常短小的、频繁调用的函数声明为内联函数,以提高执行效率。