C++中inline来替代宏函数
宏的优缺点:
优点:
1、增强代码的复用性
2、提高性能
缺点:
1、不能调试
2、没有类型安全的检查
3、有些场景下非常复杂,容易出错,不容易掌握
而C++针对C语言宏的定义函数的缺点所提出内联函数的概念。
一般情况下C++推荐用const和enum(枚举)代替宏常量,用inline去替代宏函数。
内联函数(inline)
1.内联函数概念:
以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,内联函数提升程序运行的效率。
2.内联函数的一般使用:
我们如果用宏来定义一个宏函数Add的话,非常复杂且容易出错,代码如下:
// 原函数
int Add(int a,int b)
{
return a + b;
}
// 宏函数
#define Add(x,y)((x)+(y))
由此可见宏定义函数对我们日常开发和编写十分的不便利。
所以我们采用内联函数来替代宏函数。
语法规则:原函数申明前面 + inline
代码如下:
// 内联函数
inline int Add(int a,int b)
{
return a + b;
}
调用内联函数和调用普通函数一样。
3.查看方式:
1、在release模式下,内联直接起作用。
2、在debug模式下,需要对编译器进行设置,否则不会展开(因为debug模式下,编译器默认不会对代码进行优化,以下给出vs2022的设置方式)
右键解决方案点击属性如图,在C/C++找到常规找到调试信息格式,将其选择为程序数据库(/Zi) 再找到C/C++中的优化找到内联函数的扩展,将其选择为只适用于_inine(/Ob1)。
设置完成后便可以调试在反汇编中看到调用该函数的时候,没有去call寻找函数,而是直接使用。
4.内联的优点:
(1)内联函数在被调用处进行代码展开,省去了参数压栈、栈帧开辟与回收,结果返回等,从而提高程序运行速度。
(2)内联函数相比宏函数来说,在代码展开时,会做安全检查或自动类型转换(同普通函数),而宏定义则不会。
(3)在类中声明同时定义的成员函数,自动转化为内联函数,因此内联函数可以访问类的成员变量,宏定义则不能。
(4)内联函数在运行时可调试,而宏定义不可以。
5.内联的缺点:
(1)可执行文件大小增大。
(inline是经典的空间换时间,在使用内联函数时,是将代码展开,而不是到内存栈中寻找函数的位置,容易造成代码膨胀)(例如N次使用函数时,且函数为K行代码,普通函数使用时用了N+K行,而内联函数使用时用了N*K行)
(2)inline 函数无法随着函数库升级而升级。
如果f是函数库中的一个inline函数,使用它的用户会将f函数实体编译到他们的程序中。
(3)是否内联不可控
函数是否内联取决于编译器,有时如果函数代码行数太多,编译器默认没有内联。
6.注意事项:
(1)使用函数指针调用内联函数将会导致内联失败。
函数指针需要存储函数实体地址,而inline函数直接展开,并没有在内存栈中开辟地址记录。
(2)如果函数体代码过长或者有多重循环语句,if或witch分支语句或递归时,不宜用内联。
因为代码过长时,使用内联函数的话,代码膨胀严重,可执行文件大小增大过多。并且编译器会自动地将过长的内联函数调成普通函数,压入内存栈中。
(3)类的 constructors、destructors 和虚函数往往不是 inline 函数的最佳选择。
类的构造函数(constructors)可能需要调用父类的构造函数,析构函数同样可能需要调用父类的析构函数,二者背后隐藏着大量的代码,不适合作为inline函数。虚函数(destructors)往往是运行时确定的,而inline是在编译时进行的,所以内联虚函数往往无效。如果直接用类的对象来使用虚函数,那么对有的编译器而言,也可起到优化作用。
(4)内联函数一般定义在源文件,不建议申明和定义分离到两个文件,分离会导致链接错误。因为inline被展开,就没有函数地址,链接时会出现报错。无法与正常函数一样一个在头文件一个在源文件。
内联展开是在编译时进行的,只有链接的时候源文件之间才有关系。
(5)无法强制编译器进行内联操作。
(6)如果在类中直接定义短小函数,那么默认为内联函数。
小结
C++用内联函数来取代宏定义函数。而函数内联操作是一种空间换时间的做法,它会使可执行文件大小增大。而一般我们定义较小的函数可以选择使用内联操作,来增加效率。
希望该博客能对大家有所帮助。