💓博主CSDN主页:杭电码农-NEO💓
⏩专栏分类:C++初阶之路⏪
🚚代码仓库:NEO的学习日记🚚
🌹关注我🫵带你学习C++
🔝🔝
内联函数
- 1. 前言
- 2. 内联函数概念
- 3. 内联函数的特性
- 4. auto关键字
- 5. auto的使用规则
- 5.1 对指针和引用的区别
- 5.2 一行多次定义的场景
- 5.3 auto无法使用的场景
- 6. 基于范围的for循环
- 7. 总结以及拓展
1. 前言
本章重点:
本节着重讲解内联函数的概念
和使用方法,并且介绍auto关键字,
拓展一个C++的范围for(只做了解)
2. 内联函数概念
基本概念:
- 被inline修饰的函数被称为内联函数
- 编译器会在编译时将内联函数展开
- 展开后,没有函数调用建立栈帧的开销
- 内联函数可以提升程序运行效率
举例说明:
inline int Add(int left, int right)
{
return left+right;
}
这就是一个内联函数
它的汇编代码可以简单看一下:
在函数栈帧的创建与销毁中可知
调用一个函数,开辟栈帧时需要call指令
而这里并没有call指令出现
可见内联函数和普通函数确实有不同
对比说明:普通函数
int Add(int left, int right)
{
return left+right;
}
它的汇编代码如下:
很明显这里使用了call指令
3. 内联函数的特性
特性:
- inline是一种以空间换时间的做法
如果编译器将函数当成内联函数处理
在编译阶段,会用函数体替换函数调用
缺陷:
可能会使目标文件变大
优势:
减少调用开销,提高运行效率
- inline对于编译器而言只是一个建议
若一个函数代码很长
则编译器不会将它变成内联
在《C++prime》中曾这样写到:
- 一般来说,函数代码在10行及以内时
这时编译器会将它优化为内联函数
有些编译器是在30行以内
综上所述:
定义函数为内联时
应当检查函数代码行数是否足够少
- 内联函数的定义和声明不能分开
因为inline被展开后
就没有函数地址了,链接时会找不到
内联函数的一些性质可以
在某些函数使用宏替换的地方
将函数定义为内联,也是同样的效果
4. auto关键字
在学习C++到后期的时候
经常会遇见类似于下面这种代码:
__list_iterator<InputTterator>::iterator it = tmp.begin();
这段代码看起来非常难理解
但事实上它和定义一个整型是一个意思
就是在定义一个变量
你可能会问:这和auto有啥关系?
auto是一个类型,类似于int,char
但是这个类型是由编译器自己推导出来的
什么意思?,举个例子:
int a = 10;
auto b = a;
auto c = 'a';
由于a是int类型的,将a的值赋值给b
编译器就推导出b的类型也是int
同理可得,c的类型就是char
了解了auto的用法后,来化简代码:
__list_iterator<InputTterator>::iterator it = tmp.begin();
//化简后
auto it = tmp.begin();
这两句代码意思相同
这极大的方便了我们的使用!
5. auto的使用规则
5.1 对指针和引用的区别
- 对指针来说*可有可无
请看以下代码:
int x = 10;
auto a = &x;
auto* b = &x;
a的类型是int*,此时的auto是int*
b的类型是int*,此时的auto是int
所以对于对象是指针来说
在使用auto时加不加*都无所谓
- 对引用来说必须加上&
请看以下代码:
int x = 10;
auto& c = x;
auto d = x;
c的类型是int的引用,此时的auto是int
d的类型是int,此时的auto是int
假设你想定义一个引用变量
请务必加上引用&符号
5.2 一行多次定义的场景
当在同一行声明多个变量时这些变量
必须是相同的类型,否则编译器将会报错
因为编译器实际只对第一个类型进行推导
然后用推导出来的类型定义其他变量
比如:
auto a = 1, b = 2; //没问题
auto c = 3, d = 4.0; // 该行代码会编译失败,因为c和d的初始化表达式类型不同
事实上,当第一个变量被推导成int后
第二个变量默认也是int.
但是int类型不能存放double类型的值
5.3 auto无法使用的场景
auto无法作为函数参数
void TestAuto(auto a)
{
//...
}
此处编译器无法对a的实际类型进行推导
auto无法用于声明数组
int a[] = {1,2,3};
auto b[] = {4,5,6};
感觉上,数组中都是整型
那么auto应该会自动识别为整型
但是实际上这样做是不行的,详情请看:
英文文献
6. 基于范围的for循环
C++11中支持这样遍历数组:
int arr[] = {1,2,3,4,5,6};
for(auto e : arr)
{
cout<<e <<" ";
}
其中的for循环的框架就叫做范围for
它可以这样理解:
并且范围for是可读可写的!
int array[] = { 1, 2, 3, 4, 5 };
for(auto& e : array)
{
e *= 2;
}
只需要将e的类型变为引用
就可以修改数组中元素
注:中间的:是范围for的规定写法,没有特殊意义
注意:与普通循环类似
可以用continue来结束本次循环
也可以用break来跳出整个循环
7. 总结以及拓展
本节的所有内容旨在为后面的C++
学习打基础,C++学习一环紧扣一环
环环都不能掉队!
拓展:
是不是感觉C++的范围for非常神奇?
但是实际上它是一个非常笨比的实现
它依赖一个叫迭代器的东西
底层实现上
必须有一个自定义函数叫begin()才能
满足范围for,就算你实现的功能和begin
函数一样,但是名字不一样
也不支持范围for,比如Begin
虽然C++后期的学习也会提到范围for
但是大家有兴趣可以提前了解
详情可以参考这篇文章:
C++ 中不同类型的基于范围的 for 循环迭代器