C++引用2

 什么是引用变量?

引用实际上是已定义变量别名,使一个变量拥有多个名字

c++给符号赋予了另一个意义,将其用来声明引用

int a=9;

int&b=a;

此时b成为a的一个别名,a就是b,b就是a.它们均指向同一片内存

int a=99;
int&b=a;//b成为a的别名

cout<<&a<<endl;
cout<<&b<<endl;
//a,b的地址相同

cout<<a<<endl;
cout<<b<<endl;
//a,b的值相同

a,b的各种属性相同,说明了a,b是同一个变量,该变量有a,b两个名字

引用变量的注意点

必须在声明引用变量时将其初始化

比如

int a=99;
int&b;
b=a;//这是不行的,在声明引用时必须将其初始化

int&c=a;//这是对的

引用变量一旦和某个变量关联起来,就将一直效忠于它

比如

int a=99;
int&b=a;
int c=88;
b=c;//这代表将c的值88赋给名字为b的那个变量(它也叫a)

此时b=c代表把c的值88赋给名为b的变量(这个变量也叫a),所以此时a,b,c的值均为88,其中a,b代表同一个变量

这点和int* const b=&a;类似,*b可以改,b不能改

引用只能绑定在一个左值上面,不能与字面值或者某个表达式的计算结果绑定在一起

int&a=10;//这是不行的
int&b=2+3;//这是不行的

注意点4

除了两种例外情况,所有的引用的类型都要和与之绑定的对象严格匹配 

例外情况1——对const的引用可能引用一个并非const的对象

第一种例外情况是在初始化常量引用时允许用任意表达式作为初始值,只要该表达式的结果能转换成引用的类型即可。

尤其,允许为一个常量引用绑定非常量的对象,字面值,甚至是个一般表达式:

int a=2;
const int&b=i;
const int&c=3;
const int&d=a*2;

上面这些都是允许的

 要想理解这种例外情况的原因,最简单的办法是弄清楚当一个常量引用被绑定到另外一种类型上时到底发生了什么:

double dval = 3.14;
const int &ri = dval;


此处ri 引用了一个int型的数。对ri的操作应该是整数运算,但dval却是一个双精度浮点数而非整数。因此为了确保让ri绑定一个整数,编译器把上述代码变成了如下形式

const int temp = dval; // 由双精度浮点数生成一个临时的整型常量
const int &ri = temp; // 让ri绑定这个临时量


在这种情况下,ri绑定了一个临时量(temporary)对象。所谓临时量对象就是当编译器需要一个空间来暂存表达式的求值结果时临时创建的一个未命名的对象。C++程序员们常常把临时量对象简称为临时量。

当ri不是常量时,如果执行了类似于上面的初始化过程将带来什么样的后果。如果ri不是常量,就允许对ri赋值,这样就会改变ri所引用对象的值。注意,此时绑定的对象是一个临时量而非 dval。程序员既然让 ri 引用 dval,就肯定想通过ri 改变dval的值,否则干什么要给ri赋值呢?如此看来,既然大家基本上不会想着把引用绑定到临时量上,C++语言也就把这种行为归为非法。

也就是说下面这种情况是非法的

	const int a = 9;
	int& b = a;

例外情况2——在公有继承中,我们可以将基类引用绑定一个派生类对象

class A{};
class B:public A{};

B b;
A&a=b;//这是可以的

注意是公有继承 

 正常情况

除了两种例外情况,所有的引用的类型都要和与之绑定的对象严格匹配 

int a=0;
char b=4;
int&c=b;//这是不可以的
char&d=a;//这是不可以的

将引用用作函数参数

将引用作为函数参数,使得函数中的变量名成为调用程序中的变量的别名,这也叫按引用传递。

按值传递和按引用传递的区别

按值传递

void A(int a)//调用时会创建一个名为a的临时变量,并将传入的变量的值赋给a
{
a=a+1;
cout<<a<<endl;
}
int main()
{
int w=99;
A(w);
}

执行完后w的值不变

按引用传递

void B(int&a)
//调用时给传入进来的变量起个别名a,a就能直接修改传入的原始数据了
{
a=a+1;
cout<<a<<endl;
}
int main()
{
int w=99;
B(w);
}

执行完后w的值变为100,因为a是w的别名,a+1就是w+1

总结

按值传递需要通过创建临时变量来拷贝传入的变量,修改临时变量不会影响传入的参数

按引用传递通过给传入的变量起别名来使用原始数据,不必创建临时变量,节省了栈帧开销

 临时变量,引用参数和const

按引用传递应该注意的点
#include<iostream>
using namespace std;
void A(int& a)
{
    a=1;
	cout << a << endl;
}
void B(int b)
{
	cout << b << endl;
}
int main()
{
	int a=9;
    double b=9;
	A(b);//这是不被允许的
    A(a+1);//这也是不行的
	B(b+2);//按值传递存在类型自动转换
	B(b);//同上

}

我们试图将double类型传进A函数里,按值传参会通过自动类型转换将其转换为int类型的,按引用传递不支持参数自动类型转换,

我们又试图将a+1传入函数A(),但是按引用传参的要求更高一些,按照引用定义,A函数调用时给传进来的变量起个别名为a,但是a+1不是变量,所以c++禁止这种行为 

如何解决上面这种情况呢?

我们可以将函数定义改为void  A(const  int& a),如果实参和引用参数不匹配,c++将生成临时变量,当且仅当参数为const引用时,c++才允许这么做

引用参数是const时,什么情况会生成临时变量?

1.实参的类型正确,但不是左值

左值:常规变量(变量,数组元素,结构成员,引用和解除引用的指针),const 变量

2.实参的类型不正确,但可以转换为正确的类型

过程:如果函数调用的参数不是左值或与之相应的const引用参数的类型不匹配,则c++将创建正确的匿名变量,将函数调用的参数的值传给该匿名变量,并让参数来引用该变量。

值得注意的是使用const引用参数,代表传入的参数不能被修改,同时可以接受const对象和非const对象

引用作为函数返回值

为什么要返回引用?

我们可以先看看下面这段代码

int A(int a)
{
return a-1;
}
int& B(int&b)
{
b=b-1;
return b;
}
int main()
{
int w=3;
int e=A(w);//调用时先把w-1的值先存储在一个临时位置,再赋给e
int r=B(w);//调用时直接把w-1的值复制到r上
}

我们返回引用意味着程序在内存中不产生返回值的副本提高程序运行速度

返回引用要注意的问题

1.最重要的是应避免返回函数终止时不再存在的内存单元引用(注意是避免,而不是禁止)

比如返回局部变量,指向局部变量的指针等

int& A()
{
int a=9;
return a;//a是局部变量,这是不能返回它
}
int& B()
{
int b=1;
int*c=&b;
return *c;//这也是不可以的,因为b是局部变量
}

我们可以试着调用一下它

int main()
{
int f=A();
cout<<f<<endl;
}

惊奇的发现f的值居然是 9,这是因为a虽然被销毁了,但是它的值还在,这个具体的原因这里不做探讨,感兴趣的可以去思考思考

2.其次就是要避免返回用new来分配的存储空间

int& B()
{
int*a=new int;
*a=9;
return *a;//避免返回这个,因为容易忘记用delete
}

这个就是非常容易忘记用delete来释放内存了。 

返回引用的使用方法

首先看一下怎么用return返回啊

返回什么啊?

我们一般是返回传入的参数,确保不返回局部变量

int& A(int&a)//返回类型为int&
{
a=a+3;
return a;//返回的类型是int类型或者int&类型即可,注意只能变量
}

返回引用和不返回引用的区别

我们可能在实际操作中会遇到这个疑惑啊

#include<iostream>
using namespace std;
int& A(int& a)
{
a+=2;
return a;
}
void B(int& b)
{
b+=2;
}
int main()
{
int w=2;
A(w);
cout<<w<<endl;
B(w);
cout<<w<<endl;
}

运行程序,我们发现结果是4和6,有人就纳闷了,明明A函数是有返回类型的,为什么可以直接用A(W)这样调用?我们还发现A函数和B函数都能把w的值加2,那我们为什么又要给A函数加上返回类型呢?这不是多此一举吗?A函数和B函数有什么区别?

A函数可以这样调用是因为它的返回类型是引用类型int&。当函数返回一个引用时,实际上是返回了原始变量的别名,而不是一个新的副本。所以在调用A函数时,返回的引用被赋值给了w,即w和a都指向了同一个变量。这样的调用方式可以直接修改原始变量的值。

 A函数和B函数的区别是A函数可以被嵌套使用,而B函数不可以

#include<iostream>
using namespace std;
int& A(int& a)
{
a+=2;
return a;
}
void B(int& b)
{
b+=2;
}
int main()
{
int w=2;
B(A(w));//这是可以的,因为A的返回类型刚好与B的参数类型一致
cout<<w<<endl;
A(B(w));//这是不可以的,因为A的参数类型是int&,而B的没有返回类型
cout<<w<<endl;
}

此外A函数还可以将返回的对象的值赋给另一个变量,B函数不可以

int main()
{
int w=2;
int e=A(w);//把w的值复制给e
}

实际上int e=A(w)和下面这个等价

A(w);
int e=w;

将const用于引用返回类型

我们在很多书上会看到类似下面这种代码

int& A(int& a)
{
a+=2;
return a;
}
void B(int& b)
{
b+=2;
}
int main()
{
int t=9;
int w=3;
A(w)=t;
//w先加2,然后t的值被赋给w
B(w)=t;
//这是不行的
}

为什么A函数的返回值可以放在赋值语句=的左边?而常规的不行?

这是因为A函数的返回值不是临时对象,是可以取地址的,而B的是临时对象,出了函数就销毁了

那我们如果不想给A()函数赋值的话,可以用const修饰它的返回值

const int& A(int& a)
{
a+=2;
return a;
}

int main()
{
int t=9;
int w=3;
A(w)=t;
//这是不行的

}

这样子就禁止了被赋值的情况,而其他功能又不丧失的情况 

对象,继承和引用

基类引用可以接受基类引用和派生类引用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/596217.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

虚拟键代码

虚拟键代码 虚拟键码 (Winuser.h) - Win32 apps | Microsoft Learn 在Windows操作系统中&#xff0c;虚拟键代码&#xff08;Virtual-Key Codes&#xff09;是一组用来表示键盘上按键的数值。这些代码通常用于Windows API函数&#xff0c;以便程序能够识别和处理键盘输入。 虚拟…

OSEK任务管理

1 前言 RTOS通过任务&#xff08;task&#xff09;来组织应用层程序框架&#xff08;framework&#xff09;&#xff0c;支持任务的并发和同步执行&#xff08;concurrent and asynchronous execution of tasks&#xff09;&#xff0c;并通过调度器&#xff08;scheduler&…

[方法] Unity 实现仿《原神》第三人称跟随相机 v1.1

参考网址&#xff1a;【Unity中文课堂】RPG战斗系统Plus 在Unity游戏引擎中&#xff0c;实现类似《原神》的第三人称跟随相机并非易事&#xff0c;但幸运的是&#xff0c;Unity为我们提供了强大的工具集&#xff0c;其中Cinemachine插件便是实现这一目标的重要工具。Cinemachi…

超分辨率重建——BSRN网络训练自己数据集并推理测试(详细图文教程)

目录 一、BSRN网络总结二、源码包准备三、环境准备3.1 报错KeyError: "No object named BSRN found in arch registry!"3.2 安装basicsr源码包3.3 参考环境 四、数据集准备五、训练5.1 配置文件参数修改5.2 启动训练5.2.1 命令方式训练5.2.2 配置Configuration方式训…

vivado UltraScale 比特流设置

下表所示 UltraScale ™ 器件的器件配置设置可搭配 set_property <Setting> <Value> [current_design] Vivado 工具 Tcl 命令一起使用。

翻译《The Old New Thing》 - What’s the point of DeferWindowPos?

Whats the point of DeferWindowPos? - The Old New Thing (microsoft.com)https://devblogs.microsoft.com/oldnewthing/20050706-26/?p35023 Raymond Chen 在 2005年7月6日 DeferWindowPos 的作用是什么&#xff1f; 简要 文章讨论了 DeferWindowPos 函数的用途&#xff…

LangChain框架学习总结

目录 一、简介 二、概念 三、组件具体介绍 3.1 Models 3.1.1 LLMs 3.1.2 Chat Models 3.1.3 Text Embedding Modesl 3.1.4 总结 3.2 Prompts 3.2.1 LLM Prompt Template 3.2.1.1 自定义PromptTemplate 3.2.1.2 partial PromptTemplate 3.2.1.3 序列化PromptTemplat…

IMEI引起的无法驻网问题

这篇内容没什么意思&#xff0c;仅仅是做个简单记录。 问题不复杂&#xff0c;场景很简单&#xff0c;如上图&#xff0c;UE在进行LTE attach过程时&#xff0c;在送完NAS security mode complete后&#xff0c;就立刻收到了网络attach reject 带cause 6 Illegal ME&#xff0c…

Chrome浏览器安装React工具

一、如果网络能访问Google商店&#xff0c;直接安装官方插件即可 二、网络不能访问Google商店&#xff0c;使用安装包进行安装 1、下载react工具包 链接&#xff1a;https://pan.baidu.com/s/1qAeqxSafOiNV4CG3FVVtTQ 提取码&#xff1a;vgwj 2、chrome浏览器安装react工具…

设置定位坐标+请按任意键继续

设置定位坐标 目的 在编程和游戏开发中&#xff0c;设置定位坐标的目的是为了确定对象在屏幕或游戏世界中的具体位置。坐标通常由一对数值表示&#xff0c;例如 (x, y)&#xff0c;其中 x 表示水平位置&#xff0c;y 表示垂直位置。设置定位坐标的目的包括&#xff1a; 1. **精…

【云原生】Pod 的生命周期(二)

【云原生】Pod 的生命周期&#xff08;一&#xff09;【云原生】Pod 的生命周期&#xff08;二&#xff09; Pod 的生命周期&#xff08;二&#xff09; 6.容器探针6.1 检查机制6.2 探测结果6.3 探测类型 7.Pod 的终止7.1 强制终止 Pod7.2 Pod 的垃圾收集 6.容器探针 probe 是…

MATLAB 变换

MATLAB 变换&#xff08;Transforms&#xff09; MATLAB提供了用于处理诸如Laplace和Fourier变换之类的变换的命令。转换在科学和工程中用作简化分析和从另一个角度查看数据的工具。 例如&#xff0c;傅立叶变换允许我们将表示为时间函数的信号转换为频率函数。拉普拉斯变换使…

Linux驱动开发——(十一)INPUT子系统

目录 一、input子系统简介 二、input驱动API 2.1 input字符设备 2.2 input_dev结构体 2.3 上报输入事件 2.4 input_event结构体 三、代码 3.1 驱动代码 3.2 测试代码 四、平台测试 一、input子系统简介 input子系统是管理输入的子系统&#xff0c;和pinctrl、gpio子…

#9松桑前端后花园周刊-React19beta、TS5.5beta、Node22.1.0、const滥用、jsDelivr、douyin-vue

行业动态 Mozilla 提供 Firefox 的 ARM64 Linux二进制文件 此前一直由发行版开发者或其他第三方提供&#xff0c;目前Mozilla提供了nightly版本&#xff0c;正式版仍需要全面测试后再推出。 发布 React 19 Beta 此测试版用于为 React 19 做准备的库。React团队概述React 19…

【driver4】锁,错误码,休眠唤醒,中断,虚拟内存,tasklet

文章目录 1.互斥锁和自旋锁选择&#xff1a;自旋锁&#xff08;开销少&#xff09;的自旋时间和被锁住的代码执行时间成正比关系2.linux错误码&#xff1a;64位系统内核空间最后一页地址为0xfffffffffffff000~0xffffffffffffffff&#xff0c;这段地址是被保留的&#xff0c;如果…

全新桥隧坡安全监测解决方案,24h监测效率提升30%

4月26日&#xff0c;交通运输部党组书记、部长李小鹏在部务会上强调&#xff0c;要高度重视公路桥梁隧道结构监测工作&#xff0c;抓紧推进公路桥梁隧道结构监测系统建设&#xff0c;进一步健全完善公路桥梁隧道结构监测长效运行机制。 中海达积极参与公路桥梁隧道结构监测工作…

触摸OpenNJet,感悟云原生

小程一言 云原生使得应用充分利用云计算、容器化和微服务架构等现代技术来构建和运行应用程序。 云原生技术的用处在于提高应用程序的可靠性、可伸缩性和灵活性&#xff0c;加快开发和部署速度&#xff0c;降低成本&#xff0c;提升整体的效率和竞争力。通过采用云原生技术&a…

Flink窗口理论到实践 | 大数据技术

⭐简单说两句⭐ ✨ 正在努力的小叮当~ &#x1f496; 超级爱分享&#xff0c;分享各种有趣干货&#xff01; &#x1f469;‍&#x1f4bb; 提供&#xff1a;模拟面试 | 简历诊断 | 独家简历模板 &#x1f308; 感谢关注&#xff0c;关注了你就是我的超级粉丝啦&#xff01; &a…

嵌入式学习

笔记 作业 有如下结构体 struct Student{ char name[16]; int age; double math_score; double chinese_score; double english_score; double physics_score; double chemistry…

图片浏览器-PicView

一、前言 PicView 是一款适用于 Windows 10 或 11 的快速高效的图像查看器&#xff0c;配备了干净简洁的用户界面&#xff0c;可以在不需要时方便地隐藏。 二、支持类型 它支持广泛的图像文件类型&#xff0c;包括&#xff1a;WEBP、GIF、SVG、PNG、JXL、HEIC、PSD 三、软件特…