1.内联函数的概念:
以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,内联函数提升程序运行的效率。
展开:编译器将相关内联函数的功能语句识别后放到相应位置。
类似与宏的替换,其实是宏的改进。
内联函数的定义格式:在函数的返回类型前加inline
inline int ADD(int x, int y)
{
return x + y;
}
int main()
{
int a = 6, b = 6;
cout << ADD(a, b) << endl;
return 0;
}
2.内联函数相比于宏的优越性:
C语言中的宏其实是很坑的,因为它极其容易写错,就比如说写一个最简单的ADD函数,请问以下哪个宏是对的?
实际上全都错了,第三个看起来是不是很像对的?其实在特定情况下也是错的:
输出的结果为22,这是因为加法的优先级在按位与按位或之前,所以会把中间的a和b加起来,结果自然就出错了,答案应该是:
#define ADD(x,y) ((x)+(y))
但是在内联中就不存在这样的问题了,因为内联不是单纯的替换,它是有传参的,所以内联相较于宏来说不容易出错。
归纳如下:
宏的缺点:
1.容易出错
2.不能调试
3.没有安全类型的检查(因为是直接替换)
而内联规避了这些缺点
3.内联函数的特性:
(1)inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运行效率。
(2)inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现、不是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。
也就是说,即使你写的是内联函数,但是编译器认为这个函数不适合作为内联,那么这个函数就会被忽略掉内联特性。
以下为 《C++prime》第五版关于inline的建议:一般来说,内联机制用于优化规模较小、流程直接、频繁调用的函数。很多编译器都不支持内联递归函数,而且一个75行的函数也不大可能在调用点内联地展开。
(3)inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。
4.内联函数的使用和拓展:
我们先定义一个头文件,里面写上ADD函数的声明并注明它是一个内联函数。
接着在源文件add中写下ADD函数的定义
最后在test.cpp文件中调用
点击编译,发现报了个链接错误。
这里验证了3.3内联函数不推荐声明和定义分离,接着我们添加一个func函数,由func函数间接调用ADD函数。
结果发现编译通过了:
为什么呢?我们首先要知道什么是链接错误,通俗来说链接错误就是编译器在运行到调用函数操作时,找不到这个函数,就会报链接错误。因为我们将ADD函数设置为内联函数,所以编译器就会认为这个内联函数在用的地方就直接展开了,就没有必要生成ADD函数的指令和建立栈帧并将地址及其符号放入符号表,那么既然编译器都不生成指令和地址了,而这种定义和声明分开的操作并不会展开这个内联函数,因为头文件中只有声明,而定义在另一个文件中,从声明到定义也需要链接操作,链接操作需要地址及其符号。于是乎,从声明到定义的链接操作就出了问题,因为不生成地址了又怎么能找得到ADD函数的定义呢?
后面我加入了一个func函数间接调用ADD,因为ADD函数的定义和func函数的定义在一个文件中,编译器将视为内联函数的ADD直接在func中展开,就能正常编译了。