【C++】10分钟搞懂内联函数
前言
内联函数是C++
特有的一种函数,通过添加关键字inline
到函数定义前,实现将普通函数转换为内联函数。本文旨在从以下几个方面通俗地讲解内联函数,帮助小白轻松理解和使用内联函数。
- 内联函数是啥
- 内联函数有啥用
- 啥情况下使用内联函数
内联函数是啥
直接先上定义:
- 定义:以
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; } // 需要显式内联
这样又有什么用呢?
内联函数有啥用
对比内联函数和普通函数,在效果上两者实现一致,但是在效率上内联函数要更胜一筹。究其原因,需要先理解函数调用的过程:
- 函数调用过程
- 程序需要存储当前地址,以便调用结束后返回继续执行
- 程序将传入函数的参数压栈
- 程序跳到跳到标记函数起点的内存单元,执行函数代码
- 函数调用结束后,将栈清空,返回到之前存储的地址继续执行
而内联函数就简单多了,相当于直接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被+了两次
同时,宏无法调试,但内联函数可以。在程序的调试版本,内联函数并没有真正内联,就像普通函数一样实现调试,在程序的发行版本,编译器才会实现真正内联。
由此,我们得到内联函数的几个作用:
- 提升代码执行效率,不需要跳转进行函数调用,省去参数压栈、栈帧开辟与回收,结果返回等。
- 相比于宏,有类型检查,安全性更高,具有一般函数特性,可调试
啥情况下使用内联函数
既然内联函数那么好,全部函数改成内联函数不好吗?内联函数虽然省略了函数的一般步骤,但其每次调用都会copy一份副本的特性使得整体函数代码数量增加,消耗更多空间。
下面的情况不适合使用内联:
- 函数体代码过长,内联会使得代码膨胀过大
- 函数体内存在循环或其他复杂结构,执行函数体内代码的开销远大于函数调用,没必要内联
个人理解,内联函数应该被视作简易工具函数,对于一些重复利用率高,代码数量较少的功能(例如比大小,交换位置),可以采用内联提高效率。
另外,内联函数应当放在头文件(*.h)中,一方面可以和源文件的功能函数分离,另一方面方便被所有源文件使用(不需要每个源文件一份拷贝)。
参考资料
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!