21天学会C++:Day6----内联函数

· CSDN的uu们,大家好。这里是C++入门的第六讲。
· 座右铭:前路坎坷,披荆斩棘,扶摇直上。
· 博客主页: @姬如祎
· 收录专栏:C++专题

目录

 1. 知识引入

2. 知识点讲解

2.1 内联函数的使用

2.2 内联函数的特性 

2.2 内联函数的注意点

3. 面试题


 1. 知识引入

在学C语言的宏时,我们了解了宏函数的概念与用法。我们也知道宏函数有一些易错点,稍不注意就可能出错!你若过不相信的话,就别看下面的代码自己写一个 a + b 的宏函数。

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

int main()
{
	cout << ADD(10, 20) << endl;
	return 0;
}

不知道UU们写对了吗?为什么说宏函数容易写错呢?我们知道红的特性就是替换,这种特性会使我们遇到一些难以避免的错误,我们来看看这些常见的错误:

不加外面的括号替换之后由于运算符的优先级问题,导致结果出错!

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

int main()
{
	cout << ADD(10, 20) * 2 << endl; //使用宏函数
	cout << (10) + (20) * 2 << endl; //宏函数替换之后
	return 0;
}

 下面的代码同样也存由于运算符的优先级产生的问题:加法的优先级更高,导致结果出错。

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

int main()
{
	int a = 1, b = 2;
	cout << ADD(a | b, a & b) << endl; //使用宏函数
	cout << (a | b + a & b) << endl; //宏函数替换之后
	return 0;
}

那你可能会说了:那我每次都想正确的写法那样,多加几个括号不就能避免这种错误啦!很遗憾因为宏的替换特性,有些错误难以避免,观察如下代码:

我们尝试利用一个宏函数求解 b 的平方,但是我们用 ++b 来使用该函数,替换之后 ++b 会被执行两次,显然最终的结果并不是我们想要的。

#define Square(a) ((a) * (a))

int main()
{
	int b = 2;
	cout << Square(++b) << endl; //使用宏函数
	cout << ((++b) * (++b)) << endl; //宏函数替换之后
	return 0;
}

 但是呢宏还是有优点的,下面我们就来看看宏的优缺点:

优点:宏函数不需要建立函数栈帧,可以提高调用的效率。

缺点:不方便进行调试。(预处理阶段会进行宏的替换)

           代码可读性差,可维护性差,容易误用。

           没有类型安全的检查。(宏仅仅是替换嘛)。

C++ 是既想要宏的优点,又不想要宏的缺点。因此C++引入了内联函数。

以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,内联函数提升程序运行的效率。

2. 知识点讲解

2.1 内联函数的使用

用法很简单,我们只要在一个函数返回值的前面加上 inline 关键字即可。像这样:

inline int Add(int x, int y)
{
	return x + y;
}


int main()
{
	cout << Add(2, 3) << endl;
	return 0;
}

2.2 内联函数的特性 

再来看看内联函数的概念吧:

以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,内联函数提升程序运行的效率。

这里的在调用内联函数的地方展开,指的是:内联函数经过编译之后,会直接在调用处转化成汇编代码,而不是通过 call 指令,跳转到函数的实际地址处进行函数汇编代码的执行。 

在VS2019的debug模式下,默认是不会对程序进行优化的,我们看不到内联函数的展开,但是在release模式下又不支持调试,因此我们需要进行相关的设置才能看到内联函数的展开。

在项目---->属性中更改下面的设置即可 

我们通过调试下面的代码,然后查看汇编代码之后发现 Add 函数的调用并不是 call 函数地址而是直接在函数调用的地方展开,变成了汇编代码。

1. inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会
用函数体替换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运
行效率。
2. inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建
议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不
是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性

2.2 内联函数的注意点

通过上面的调试过程我们直到内联函数是直接在调用处展开的,因此内联函数并没有函数地址。也就是说,内联函数不会出现在符号表中,也就意味着内联函数不可使用函数定义与函数声明分开的写法。

 为什么会报错呢?UU们不妨结合编译,链接的相关知识想一想。 

Add函数是内联函数,在调用Add函数的地方,编译器会在调用的地方展开。但是在调用Add函数的源文件中,只有Add函数的声明。 编译器无法做到在调用的地方展开,于是编译器会尝试通过 call 指令去调用函数,但是内联函数不会在符号表中出现,就会找不到函数的地址。因此会出现链接错误。

因此我们得出一个重要的结论:内联函数的声明与定义不可分离。

那应该怎么解决这个问题呢?很简单,直接在头文件里面写内联函数的定义即可。

3. 面试题

 宏的优缺点?
优点:
1.增强代码的复用性。
2.提高性能。
缺点:
1.不方便调试宏。(因为预编译阶段进行了替换)
2.导致代码可读性差,可维护性差,容易误用。
3.没有类型安全的检查 。
C++有哪些技术替代宏?
1. 常量定义 换用const enum
2. 短小函数定义 换用内联函数

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

姬如祎

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值