【C++】10分钟搞懂内联函数

前言

内联函数C++特有的一种函数,通过添加关键字inline到函数定义前,实现将普通函数转换为内联函数。本文旨在从以下几个方面通俗地讲解内联函数,帮助小白轻松理解和使用内联函数。

  1. 内联函数是啥
  2. 内联函数有啥用
  3. 啥情况下使用内联函数

内联函数是啥

直接先上定义:

  • 定义:以inline关键字修饰定义的一类函数,让编译器用函数代码替换函数调用

听着有点玄乎,看下面一个实例:

  • 实例:
    inline int max(int a, int b){
    	return a > b ? a:b;
    }
    
    int main(){
    	int a = 3,b=4;
    	cout<<max(a,b)<<endl;  // <=> cout<<a > b ? a:b<<endl;
    }
    哦豁,内联函数做的事情就是在程序调用的时候把代码更换了。所有上面cout<<max(a,b)<<endl;的效果等价于cout<<a > b ? a:b<<endl;

除了上面的定义方式,还有类内的函数会被隐式定义成内联函数,具体如下:

// 声明1(加 inline,建议使用),光声明无法内联,需要在定义中添加inline
inline int functionName(int first, int second,...);

// 声明2(不加 inline)
int functionName(int first, int second,...);

// 定义
inline int functionName(int first, int second,...) {/****/};

// 类内定义,隐式内联
class A {
    int doA() { return 0; }         // 隐式内联
}

// 类外定义,需要显式内联
class A {
    int doA();
}
inline int A::doA() { return 0; }   // 需要显式内联

这样又有什么用呢?

内联函数有啥用

对比内联函数和普通函数,在效果上两者实现一致,但是在效率上内联函数要更胜一筹。究其原因,需要先理解函数调用的过程:

  • 函数调用过程
  1. 程序需要存储当前地址,以便调用结束后返回继续执行
  2. 程序将传入函数的参数压栈
  3. 程序跳到跳到标记函数起点的内存单元,执行函数代码
  4. 函数调用结束后,将栈清空,返回到之前存储的地址继续执行

而内联函数就简单多了,相当于直接copy一份函数体内代码,在执行到的时候直接按照代码顺序执行,省去了许多函数调用过程,节省了时间。

诶,是不是和有点像啊,例如我们下面的宏定义:

#define MAX(a,b)  a>b?:a:b

实现了和我们上面例子一样的效果。

那为什么不直接用宏?还要整个内联函数。首先,宏能够表达的意思有限,通常是一行的表达式。其次,用宏的安全性不高,容易出错。

还是上面的例子,假设定义宏如下:

#define MAX(a,b)  a>b?:a:b

那么语句
res = MAX(i,j)+2;

会被预处理器扩展为
res = i > j? i:j+2;

由于+的优先级高于?,因此最终比较结果与我们期望不符。
那如果把宏修改为
#define MAX(a,b) (i > j? i:j)

仍然存在问题,例如
res = MAX(i++,j)
res = (i++ < j? i++:j);  // i被+了两次

同时,宏无法调试,但内联函数可以。在程序的调试版本,内联函数并没有真正内联,就像普通函数一样实现调试,在程序的发行版本,编译器才会实现真正内联。

由此,我们得到内联函数的几个作用:

  1. 提升代码执行效率,不需要跳转进行函数调用,省去参数压栈、栈帧开辟与回收,结果返回等。
  2. 相比于宏,有类型检查,安全性更高,具有一般函数特性,可调试

啥情况下使用内联函数

既然内联函数那么好,全部函数改成内联函数不好吗?内联函数虽然省略了函数的一般步骤,但其每次调用都会copy一份副本的特性使得整体函数代码数量增加,消耗更多空间。

下面的情况不适合使用内联:

  1. 函数体代码过长,内联会使得代码膨胀过大
  2. 函数体内存在循环或其他复杂结构,执行函数体内代码的开销远大于函数调用,没必要内联

个人理解,内联函数应该被视作简易工具函数,对于一些重复利用率高,代码数量较少的功能(例如比大小,交换位置),可以采用内联提高效率。

另外,内联函数应当放在头文件(*.h)中,一方面可以和源文件的功能函数分离,另一方面方便被所有源文件使用(不需要每个源文件一份拷贝)。

参考资料