C++中的inline函数(内联函数)

前言:我们在C中学习宏的时候,就体会到了宏在编写的时候需要注意非常多的细 节,否则会导致出错,而C++在设计的时候便为解决C语言在一些情况下不方便而提出了inline(内联函数)这一新特性

1 ADD宏函数

ADD函数宏的写法有以下四种:

  • 1 #define ADD(int a, int b) return a + b;
  • 2 #define ADD(a, b) a + b;
  • 3 #define ADD(a, b) (a + b)
  • 4 #define ADD(a, b) ((a) + (b))
    你觉得上面哪种是对的?我们一个一个的进行分析
1.1 #define ADD(int a, int b) return a + b;

我们首先要说明的是宏函数本质就是一种替换而不是函数,这样写在语法的层面 上就是错误的

1.2 #define ADD(a, b) a + b;

所以我们一般定义宏函数的时候不在后面加;

并且注意在这个例子里面就算不加;也是一个错误的写法

按理来说我们希望得到的是(1+2)*(2+3)=15,那为什么结果是8呢?

还是要抓住宏函数就是一个替换的规则ADD(1,2)*ADD(2,3)被替换为1+2\*2+3=8,我们可以看出我们没有保证每一个ADD宏替换后的独立。

1.3 #define ADD(a, b) (a + b)

所以这个写法也是错误的,假如ab其中是一个表达式的话,这个宏函数的替换就会违背我们预想的逻辑。

1.4 #define ADD(a, b) ((a) + (b))

这样的写不仅保证了ADD宏函数的独立性而且保证了ADD宏函数参数ab的独立性,是一个正确的宏函数。

小结:我们可以看出写对一个宏函数要注意的细节还是很多的,一不留神就容易出现问题。但是宏函数能够在预处理阶段替换,不用建立函数栈帧,可以提效(提高效率),而我们C++inline就可以继承这些优点并且屏蔽实现细节
在回顾宏后,接下来我们正式进入inline函数的学习

2 inline(内联函数)

2.1 liline的基本概念和特征
  • inline修饰的函数就是内联函数
  • 编译阶段,编译器会将内联函数展开(类似于宏替换),这样函数就不会创建栈帧,从而提高效率
  • inline修饰的内联函数是否展开取决于编译器,inline修饰函数这是给编译器一个建议,并且不同编译器对内联函数展开情况各不相同,因为这是C++标准未定义的。

关于内联函数展开的说明:内联函数的展开就是在编译阶段,编译器会在这个位置直接插入函数代码(并不是原封不动的插入 ,要根据上下文逻辑进行必要的转换和适配 ,以保证代码语义的正确性)

宏的实现需要我们自己去实现,而liline内联函数编译器帮助我们处理 ,但编译器又不是特别智能,它只能处理一些短小函数 ,对于代码较多的,递归函数即使加上inline也会被编译器忽略。
vs编译器下debug版本默认不展开inline,我们想要实现liline需要设置

2.2 vs设置inline选项


设置好后我们就可以通过汇编看内联函数非内联函数 的区别了

普通add函数:

内联add函数:

我们可以明显看出在内联函数中inline没有创建函数栈帧,并且都被展开在main函数的cout << add(x, y) << endl;的这里,我们可以看到内联函数的这一块汇编代码 明显比普通函数

2.3 inline声明和定义不能分离

inline声明和定义分离会产生链接错误,inline被展开其就没有函数地址,会在链接的时候出错

我们要理解这个,必须先简单的回顾下编译和链接的过程

F.h中有f1f2函数的声明,而在F.cpp中有这两个函数的实现,那我们要在Test.cpp文件中使用这两个函数涉及到什么过程呢?

  • 预处理:首先预处理将头文件F.h内容添加到两个cpp文件中。(预处理阶段还做了其它事)
  • 编译:检查语法生成汇编代码,这时候编译器已经将F.cpp中的f1f2函数编译并有其函数地址并记录在符号表中,而由于Test.cpp函数中只有两个函数的声明,所以使用这两个函数的位置编译的时候两个函数的地址并未填入(等待编译的时候补充)
  • 汇编:将汇编代码转化为二进制机器指令
  • 链接:将两个cpp的目标文件合并为.exe的可执行文件,这个时候Test.o(linux下)目标文件便去F.o符号表中找f1f2函数地址,以填补在汇编指令中call 指定函数地址空缺。

而被inline修饰的内联函数具有内部链接属性,它不会出现在符号表中,如果f1或者f2是内联函数那么Test.o就无法从符号表中获取函数地址从而找到函数完成调用,从而导致链接错误。

inline的内联函数一样,static修饰的函数也具有内部链接属性(函数只能在该文件使用,其它文件找不到)

extern可以声明函数的存在,从而让编译器不报错,并在后面的链接阶段从其它文件的符号表中去寻找函数地址,我们可以看到函数被正确使用。

static修饰后:

发生了链接错误,static将函数有外部链接属性变成内部链接属性

总结:符号表就是各个文件调用其它文件的说明书,而inlinestatic会将函数从这个说明书(符号表)去除,从而让文件无法使用其它文件的函数(就是不知道这个函数的地址)。

那内联函数这么好,我们是不是凡是个函数都给前面加inline变成内联函数呢?

答案当然是不可以,我们可以通过一个场景来看

inlinefunc函数被频繁的调用的话,每个位置都被展开,那么整个程序的代码量便会大大增加,会导致程序运行变慢。

所以我们一般将调用频繁且短小函数设置为内联函数。

相关推荐
清风wxy2 小时前
Duilib_CEF桌面软件实战之Duilib编译与第一个界面程序
c++·笔记·ui·mfc
郝学胜-神的一滴2 小时前
Linux下,获取子进程退出值和异常终止信号
linux·服务器·开发语言·c++·程序人生
notfindjob2 小时前
MFC动态加载图片
c++·mfc
CodeByV2 小时前
【C++】继承
开发语言·c++
乱舞八重击(junluoyu)4 小时前
1.PagedAtteion算法
c++
2301_803554524 小时前
C++ 锁类型大全详解
开发语言·c++
曼巴UE54 小时前
UE5 C++ Slate 画曲线
开发语言·c++·ue5
ue星空4 小时前
UE5C++UKismetMathLibrary源代码
c++·ue5
minji...4 小时前
C++ 面向对象三大特性之一---多态
开发语言·c++