C C++最全C++入门(三)引用,内联,2024年最新字节C C++高工面试

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

}


**可以减少拷贝,提高效率**



做返回值

函数的返回值是一个临时变量

先来看一个例子:

e9693cceaa6d42c7937d3f47b04db083.png

Count函数的返回值,直接引用是不通过的

因为函数是先调用,完成后n会销毁,因此传给ret的是一个临时变量

而临时变量具有常性,要引用前面必须加const

如下:

52833f86bbf1489183703c1bbd4a8c0a.png

引用作为返回值

859a3f589b044566bbc6ffd4bd078d4e.png

函数返回类型是一个引用的话,那么返回的就相当于是 n 的别名,别名是不用开辟临时变量的。

用引用 ret 接收函数的返回值,此时的 ret 相当于是返回值  n  别名的别名。ret 和 n 是同一个地址。

证明:

c08572a761e44426a3ccb60a8a695048.png

但是上述代码是不正确的

存在非法访问!!!!

函数先调用,调用完后赋上新的别名,但是注意,函数调用完后,变量n就已经销毁了,空间已经还给系统了,但是我们通过别名访问到了已经还给系统的空间,属于非法访问

再次调用,就会发现同样的地址,其中的内容已被系统更新

4174e59ff2224adf910222be28762602.png

通过上述内容,我们知道,引用返回可以减少拷贝,提高效率,但是我们如何防止这种 “野引用”问题的出现

保证返回变量不销毁,内存空间不还给系统即可,可以用到static函数

5e03a4c64fb94ceca069ecb509887a08.png

总结

如果函数返回时,出了函数作用域,如果返回对象还未还给系统,则可以使用引用返回,如果已经还给系统了,则必须使用传值返回,防止出现越界问题。

和指针的比较

区别

1.引用在定义时必须初始化,指针无要求

2. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体

3. 没有NULL引用,但有NULL指针

4. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数

5. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小

6. 有多级指针,但是没有多级引用

7. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理

8. 引用比指针使用起来相对更安全

底层相同

92aa50c51f2c4180b4fcf34fa2c9fee9.png

二、内联函数

概念

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

在c语言中,如果一个函数多次调用,那么每次调用都要开辟栈帧,效率会大幅降低。c语言的解决方案是用宏来实现函数的功能,在调用时直接替换展开,就没有了压栈开销,提高效率。

为什么要用inline?

在c中,函数是容易写的,但是用宏来实现函数,往往会出现问题,最常见的就是替换后的优先级问题。

例如:ADD函数
int ADD(int x,int y)
{
	int sum = x + y;
	return sum;
}

用宏来实现:

#define ADD(x,y) ((x)+(y))

错误示范:

//和函数搞混 ,分号滥用
//#define ADD(int x,int y) return x+y;

//#define ADD(x,y) return x+y;

//#define ADD(x,y) (x+y);

//不带括号,会出现优先级问题
//#define ADD(x,y) x+y

而c++中 inline 的出现就是为了解决宏难理解,且容易写错的问题。

使用示例

直接在函数前加 inline 即可,不容易写错。

inline int ADD(int x,int y)
{
	int sum = x + y;
	return sum;
}

inline可调试

c语言中宏是不支持调试的

但c++中的inline是可以调试的,需要改一些属性。

在Debug版本下,inline 和 宏 在编译中都不会展开,直接是 call ADD

c5e2c38ef9824e60959414b671963072.png

更改如下

3c192178e5b5432985819bad6e69f3f9.png

ce6f46f93989419990336a318e3794a4.png

197a9792bf8548fb9e3902923d9b253c.png

完成上述操作后,再次进行调试,转到反汇编:

对宏,没有什么改变

4b935f3406b44e4ca7f1f770e4ffa8ed.png

对inline,可支持调试

35d04e76310843d59dc1f71ed7be5e92.png

特性

1.inline是一种以空间换时间的做法,省去调用函数开辟栈帧。代码很长或者有循环/递归的函数不适宜使用作为内联函数。

2.编译器会自动优化,如果定义为inline的函数体内有循环/递归等等,编译器优化时会忽略掉内联。

dafc72049e6f40d68063b67de4954ba5.png

454b79e1df3d479cb78a02ddb523bd15.png

3.inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。

a478f28b026c47b3a69e6ea166be967a.png

解决方案,将声明和定义写在一起

1a695ba84d30454cb0aba9845cda0949.png

或者

34e8a794207b4489b5342f723c26f36d.png

再调用

1bd8c441ccdf438b8086be2d3bfd9bf0.png

三、auto关键字

定义

在c和早期c++中auto关键字几乎不怎么用,只是用来修饰局部变量,并且常被忽略,如下:

21624e36ebbe424cb7e4129d7d5444d5.png

在c++11中,auto 有了新的功能:

作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。

意思就是,当我们不知道一个变量的类型时,将他前面的类型替换为auto,编译器会根据自动推导出此数据的类型。

用法

一般例子
#include <iostream>
using namespace std;

int Test()
{
	return 6;
}


int main()
{
    //这里编译器会自动识别a是int类型
	auto a = 2;

	double b = 3.8;
    
    //编译器会自动识别出c是double类型
	auto c = b;

    //自动推出ret是int类型
    auto ret = Test();  
   
	return 0;
}
注意

不能直接auto+变量,不允许没有初始化的变量使用auto

例如:

1c4c1eb935be48158ad6fed3042fd9e3.png

即使直接使用a也不行

2a7bbd55a24e4d4abf6f368e8e2381eb.png

输出变量类型

如何判定编译器已经识别出来变量的类型呢?

记住输出变量类型的固定格式:

typeid(变量名).name()

769fc5ed7cfc42e29993142e70b59f67.png

指定auto的类型

指定auto为指针和引用

035a76ccdd1e4ae4bd353c7965d3bc0a.png

同一行定义多个变量

变量必须是相同的类型
否则会报错

2ac39f9b4f634937934e24673461719a.png

编译器会推导出第一个的变量的类型并使用,会产生冲突

定义时变量须是相同类型

auto正确用法

1.当类型特别长时,可以使用auto让编译器直接进行推导,这里在之后的笔记中会记录下来。

2.范围for循环

范围for循环

c916fadd897446c89883168487879016.png

依次自动取数组中的数据,赋值给变量e,自动结束,更加方便

在这里可以对比一下更改数组内容时的操作

用c语言的for更改数组的值

ea7df03ff1ed442daf91c5dac97f9070.png

那么c++中局部for循环可以直接做到吗?

7f1c269b56fd4a489bea7091adfb2e30.png

原因:

e是一个变量,对变量进行操作,是不影响原数组中的数据的,因此这样写,数组中的数据不会被影响。

变量可以随便取,不用非得是e

解决方法:引用

既然e是变量,对原内容无影响,那么可以将变量变为数组元素的别名

对e操作即是对数组内容的操作

方法如下:

bf80900b898946c89133d298d7da06a3.png

范围for循环必须使用auto吗?

不是的,只要是数组元素类型即可,整型数组–int ,字符类型–char ……

什么数组类型都可以用auto,更加统一方便

auto不能推导的场景

1.不能做函数参数和函数返回值

// 编译失败
void Test(auto a)
{}

//auto不能作为形参类型,因为编译器无法对a的实际类型进行推导

e83551d124d1468f9255b3a2714b0134.png

2. auto不能直接用来声明数组

void Test()
{

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

(auto a)

{}

//auto不能作为形参类型,因为编译器无法对a的实际类型进行推导


![e83551d124d1468f9255b3a2714b0134.png](https://img-blog.csdnimg.cn/e83551d124d1468f9255b3a2714b0134.png)




2. auto不能直接用来声明数组

void Test()
{

[外链图片转存中…(img-sSC6hoUP-1715682559575)]
[外链图片转存中…(img-5YUwXxIo-1715682559575)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 23
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值