浅谈C++|引用篇

目录

引入

一.引用的基本使用

(1)引用的概念:

(2)引用的表示方法

(3)引用注意事项

(4)引用权限

二.引用的本质

三.引用与函数

 (1)引用做函数参数

 (2)引用做函数返回值

四.常量引用

五.引用与指针


引入

绰号,又称外号,是人的本名以外,别人根据他的特征给他另起的名字,大都含有亲昵、憎恶或开玩笑的意味。那么在C++中,为了可以给一个已存在的变量取别名,产生了引用。引用就是给已经存在的变量取别名。

 一.引用的基本使用

(1)引用的概念:

引用并不是新定义一个变量,而是给已经存在的变量取别名,编译器不会为引用变量开辟新的内存空间,它和它引用的变量共用一块内存空间

(2)引用的表示方法

类型 & 别名 = 原名

小白: &不是按位与吗?

小明: 这是其实是运算符重载。通过重载,同一个运算符将会有不同的含义。编译器会通过上下文来确定运算符的含义。

小白: 啊,原来如此,我说&怎么还是取地址符呢,懂了。

代码实例

#include <iostream>
using namespace std;
int main() {
	int money = 99999;
	int& money_1 = money;
	cout << "money=" << money<<endl;
	cout << "money_1=" << money_1<<endl;
	return 0;
}

小白: 果然他们的值是相等的,那怎么确定他们共用一块内存空间呢?

小明: 我们一起来调试看一下。

 小明: 看,引用和它引用的对象是同一块地址。

小白:  学会了,小明厉害啊。

(3)引用注意事项

1. 引用在 定义时必须初始化

2. 一个变量可以有多个引用,但一个引用只能引用一个变量

3. 引用一旦初始化,就不可更改

1.引用在定义的时候必须初始化

由于引用是对已经存在的变量进行取别名,因此使用引用时必须指定变量(初始化)。

int& a;//错误,未初始化

 2.一个变量可以有多个引用,但一个引用只能引用一个变量

在C++语法中,一个变量有多个引用,就类似于一个人可以有多个外号。但是一个引用变量只能指向一个引用对象。

代码实例:

#include <iostream>
using namespace std;
int main() {
	int x = 100;
	int& y = x;
	int& z = x; 
	return 0;
}

3. 引用一旦初始化,就不可更改。 

引用一旦指向引用对象后,不可以更改引用对象。


小白: 啊啊啊,好多内容,脑袋记不住啊!

小明: 不急慢慢来,你哪里不明白?

小白: 为什么一个引用只能引用一个对象?

小明: 你想,如果一个引用类型同时是两个对象的别名,那计算机编译时怎么知道应该用哪一个呢?

小白:也就是说,同时指向多个对象时,具有二义性

小明:对的。

小白:那为什么初始化后,不能更改引用对象啊?

小明:这个涉及引用的本质,文章后面会讲到的。

小白 :好的,我会继续努力的。

(4)引用权限

引用原则:对原变量的引用,权限不能放大。

 代码实例:

#include <iostream>
using namespace std;
int main() {
	int x = 100;
	int& y = x;
	//权限不变可行
	const int& z = x; 
	//权利缩小可行
	return 0;
}

权限没有扩大可行

代码实例:

#include <iostream>
using namespace std;
int main() {
	const int x = 100;
	int& y = x;
	//权限扩大可行
	const int& z = x; 
	//权利不变可行

	return 0;
}

存在权限扩大不可行。

小白:这个我知道,就像班长指挥军长干活,军长上去就是一脚。

小明:哈哈哈哈,对,权限不能扩大,只能缩小


二.引用的本质

引用的本质是C++内部实现的一个指针常量

代码实例: 

#include <iostream>
using namespace std;
int main() {
	int a = 10;
	int& b = a;
	//本质:int* const b=&a
	b = 100;
	//本质:*b=100;
	return 0;
}

小白:原来如此,这就解释了为什么初始化后,不能更改引用对象了。

小明:对的,因为const修饰b,b中存储的值不能改变,因此b指向的内存地址不能改变,所以引用初始化后不能更改引用对象。

小白:这个引用本质,还是有点糊涂......

小明:你可以这样理解,当代码编译到int& b=a时,编译器其实执行int* const b=&a,当执行b=100时,编辑器实际执行*b=100

小白:我可以理解为这两种写法是一样的,只是引用简化了吗?

小明:可以的。

三.引用与函数

(1)引用做函数参数

作用:函数传参时,可以利用引用的技术让形参修饰实参

优点:可以简化指针修改实参,也更安全

一般认为函数传参有两种形式,值传递和地址传递。值传递的形参是对实参的一份临时拷贝。地址传递是传递指针,通过指针访问实参所在的内存区间。所以地址传递不用开辟新空间,因此速度更快,还能减少内存。

值传递代码实例:

#include <iostream>
using namespace std;
void swap(int x, int y) {
	int temp = x;
	x = y;
	y = temp;
	cout << "形参:" << "x=" << x << ' ' << "y=" << y << endl;
}
int main() {
	int x = 10, y = 20;
	swap(x,y);
	cout << "实参:" << "x=" << x <<' ' << "y=" << y << endl;
	return 0;
}

 值传递不改变实参的值

 地址传递代码实例:

#include <iostream>
using namespace std;
void swap(int* x, int* y) {
	int temp = *x;
	*x = *y;
	*y = temp;
	cout << "形参:" << "x=" << x << ' ' << "y=" << y << endl;
}

int main() {
	int x = 10, y = 20;
	swap(&x,&y);
	cout << "实参:" << "x=" << x <<' ' << "y=" << y << endl;
	return 0;
}

 地址传递可以改变实参的值

 小白:理解地差不多了,不过值传递的形参是对实参的一份临时拷贝是什么意思?

小明:我画个图你就知道了

 小白:明白了,值传递就是从新开辟空间,只是内容一样而已

小明:对的。

小白:那地址传递呢?

小明:地址传递就是利用指针,形参是存储实参的指针,解引用就找到实参了。

小白:明白了,那引用做形参又是怎么回事?

小明:你忘了刚学的引用本质了?

小白:哦我明白了,这个代码让我来,嘿嘿。

#include <iostream>
using namespace std;
void swap(int& x, int& y) {
	int temp = x;
	x = y;
	y = temp;
	cout << "形参:" << "x=" << x << ' ' << "y=" << y << endl;
}

int main() {
	int x = 10, y = 20;
	swap(x,y);
	cout << "实参:" << "x=" << x <<' ' << "y=" << y << endl;
	return 0;
}

小明:没错,引用当形参就是这样写的。

小明:你能根据引用本质写出指针版本吗?

小白:小问题啦,看我大展身手。

 小白:怎么样,我写的对吗?

小明:对的,其实引用也是地址传递,只是因为const的缘故,不能改变指向的对象而已。

小白:我明白了。

小明:相比指针,引用更加简便,而且因为const,比指针更加安全

(2)引用做函数返回值

引用作为函数返回值特性:

1.不能返回局部变量的引用。

2.函数的调用可以作为左值

 小白:为什么引用不能返回局部变量的引用啊?

小明:引用本质是指针,是指针常量。因为指针不能返回局部变量的地址。

小白:指针不能返回局部变量的指针吗?

小明:是的,因为局部变量会在函数执行完之后销毁,该变量的内存会返回给系统,此时函数返回该变量地址,并且调用的话,属于非法访问,是失效指针。

小白:哦哦,明白了,也就是函数执行完之后,该地址不属于该程序了,再次访问是非法的。

小明:对的。

代码实例:

#include <iostream>
using namespace std;
int& test() {
	int n = 0;
	n = 100;
	return n;
}
int main() {
	int& res = test();
	cout << res << endl;
	cout << "系统清除" << endl;;
	cout << res<<endl;
	return 0;
}

注意 :

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

小白:那只能创建全局变量,返回全局变量了吗?

小明:其实可是使用static关键字,static关键字创建在全局区(静态区),生命周期是整个程序。

小白:static创建的变量,程序不停止,变量不销毁对吗?

小明:对的。

小白:还是有些懵。

小明:不急,我们来看一下代码就懂了。

代码实例:

#include <iostream>
using namespace std;
int& test() {
	static int n = 0;
	n = 100;
	return n;
}
int main() {
	int& res = test();
	cout << res << endl;
	cout << "系统清除" << endl;
	cout << res<<endl;
	return 0;
}

小白:懂了,只要返回值在函数调用不销毁就可以是吗?

小明:对的。

小白:那函数的调用可以作为左值,又是什么意思?

小明:就是说函数可以放到等号左边,给函数赋值

小白:给函数赋值,不可能吧?

小明:实际上,函数返回值是引用时,会产生一个引用变量,只是没有名字,给函数赋值,实际就是给该引用赋值。

小白:还是不明白。

小明:我们来看代码。

代码实例:

#include <iostream>
using namespace std;
int& test() {
	static int n = 0;
	n = 100;
	return n;
}
int main() {
	int& res = test();
	test() = 99;
	cout << res<<endl;
	return 0;
}

小白:哦哦,也就是说此时函数相当于一个匿名的引用,函数当左值相当于通过匿名引用修改n的值。

小明:对的,那你看看下面代码什么意思。

代码实例:

#include <iostream>
using namespace std;
int& test() {
	static int n = 0;
	n = 100;
	return n;
}
int main() {
	int res = test();
	test() = 99;
	cout << res<<endl;
	return 0;
}

 小白:int类型能接收int&类型的值吗?

小明:可以的,其实相当于吧匿名引用解引用后,把值传给res。

小白:还是不明白。

小明:我们接着看代码。

代码实例:

#include <iostream>
using namespace std;
int& test() {
	static int n = 0;
	n = 100;
	return n;
}
int main() {
	/*int res = test();*/
	int& m = test();
	int res = m;
	test() = 99;
	cout << res<<endl;
	return 0;
}

 小明:这个明白吧。

小白:这个明白。

小明:那么换成匿名引用不懂了?

小白:懂了,也就是直接通过,匿名引用传值给变量

小明:对的。

四.常量引用

const 类型 & 别名 = 原名

1.可以直接指向一块常量。

2.函数形参列表中,可以防止形参改变从而改变实参。

小白:这个我懂相当于指向常量的指针常量。

小明:没错,例如const int* const p;p的值不能改变,*p的值也不能改变。

小白:可以直接指向一块常量,是什么意思?

小明:其实常量保存在常量区,声明周期和程序共存亡,并且常量区只有只读权限,没有写权限。如果用普通的引用,由于引用可以更给指向内存的值,属于权限扩大,会报错,使用常量引用则引用也只有读权限,则可以指向常量区。

小白:有些糊涂。

小明:没事,我们来看看代码。

代码实例:

#include <iostream>
using namespace std;
int main() {
	/*int& res = 10;*/
	const int& res = 10;
	cout << 10;
	return 0;
}

 小白:明白了,防止实参改变,从而改变实参,是为了防止手误没小心改变形参吧。

小明:厉害,直到抢答了,某些场景我们只需要值,但不许要改变引用的值,可以使用常量引用,防止粗心改变形参的值。

代码实例

#include <iostream>
using namespace std;
void text(const int& x) {
	//x = 100;
	cout << x;
}
int main() {
	/*int& res = 10;*/
	text(9);
	return 0;
}

 五.引用与指针

引用其实就是指针,引用的本质是C++内部实现的一个指针常量,建设建立引用int& b=a时,编译器其实执行int* const b=&a,当执行b=100时,编辑器实际执行*b=100。之后遇到的b可以都看成指针常量的*p,但也要看上下文。其实引用也是占用空间的,只是被C++隐藏了。 

宏观区别:
1.指针的值可以为空,但是引用的值不能为NULL,并且引用在定义的时候必须初始化;。
2. 引用在初始化时引用一个实体后,就不能再引用其他实体(指针常量),而指针可以在任何时候。指向任何一个同类型实体。
3. 在sizeof中含义不同:引用结果为引用类型的大小(sizeof(*p)),但指针始终是地址空间所占字节个数(4/8)。
4. 引用自增即引用的实体增加1((*p)++),指针自增即指针向后偏移类型的大小的字节。
5. 有多级指针,但是没有多级引用。
6. 访问实体方式不同,指针需要显式解引用,引用编译器编译器自行处理。
 

理解引用的本质,这些理解起来其实都会简单很多啦。

(其余浅谈C++系列持续更新中,请三连关注,本篇完)

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

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

相关文章

【k8s系列】一分钟搭建MicroK8s Dashboard

本文基于上一篇文章的内容进行Dashboard搭建&#xff0c;如果没有看过上一篇的同学请先查阅上一篇文章 k8s系列】使用MicroK8s 5分钟搭建k8s集群含踩坑经验 使用MicroK8s搭建Dashboard很简单&#xff0c;只需要在Master节点按照以下几步操作 1.启用Dashboard插件 microk8s en…

【软件工程】软件工程期末考试复习题

软件工程期末考试试题及参考答案 一、单向选择题 1、软件的发展经历了&#xff08;D&#xff09;个发展阶段。 一二三四 2、需求分析的任务不包括&#xff08;B&#xff09;。 问题分析系统设计需求描述需求评审。 3、一个软件的宽度是指其控制的&#xff08;C&#xff0…

[进阶]TCP通信综合案例:群聊

代码演示如下&#xff1a; 客户端&#xff1a; public class Client {public static void main(String[] args) throws Exception{System.out.println("客户端开启&#xff01;");//1.创建Socket对象&#xff0c;并同时请求与服务端程序的连接。Socket socket new…

新人拿到一个web项目如何使用idea发布运行

本文描述的是一个新手&#xff0c;拿到一个web项目&#xff0c;使用idea如何发布运行。项目中没有非常复杂的元素&#xff0c;只是试着描述应该如何配置相关内容。 内容描述前提&#xff0c;首先请您确认tomcat已经安装&#xff0c;其次确认jdk已经安装&#xff0c;并明确他们在…

STM32速成笔记—GPIO

文章目录 一、什么是GPIO二、GPIO的输入/输出模式三、GPIO初始化配置四、Boot引脚五、一些特殊的GPIO六、点亮LED1. 硬件电路2. 拉高/拉低GPIO3. 程序设计 七、GPIO的位带操作 一、什么是GPIO GPIO(英语:General-purpose input/output)&#xff0c;通用型之输入输出的简称&…

Java与SpringBoot对redis的使用方式

目录 1.Java连接redis 1.1 使用Jedis1.2 使用连接池连接redis1.3 java连接redis集群模式 2.SpringBoot整合redis 2.1 StringRedisTemplate2.2 RedisTemplate 1.Java连接redis redis支持哪些语言可以操作 &#xff08;去redis官网查询&#xff09; 1.1 使用Jedis (1)添加jedis…

【数字图像处理】2.几何变换

目录 什么是几何变换&#xff1f; 为什么要对图像进行几何变换&#xff1f; 2.1 仿射变换&#xff08;二维&#xff09; 2.2 投影变换&#xff08;三维&#xff09; 2.3 极坐标变换 2.3.1 将笛卡尔坐标转化为极坐标 2.3.2 将极坐标转换为笛卡尔坐标 2.3.3 利用极坐标变…

汇编学习教程:寻址大总结

前言 在上篇博文中&#xff0c;我们主要学习了一个全新的寄存器&#xff1a;bp。bp 寄存器在功能和使用上与 bx 有着异曲同工之妙&#xff0c;只不过两人绑定的服务对象不同&#xff1a;bx 默认绑定的是 DS 段寄存器&#xff0c;而 bp 默认绑定的是 SS 段寄存器。bx 和 bp 有着…

Unity之透明度混合与ps的透明度混合计算结果不一致

一、问题 前段时间学习shader时发现了一个问题&#xff0c;一张纯红色透明度为128的图片叠加在一张纯绿色的图片上得出的结果与ps中的结果不一致。网上查找了ps中的透明混合的公式为 color A.rgb*A.alpha B.rgb*(1-A.alpha)。自己使用代码在unity中计算了一下结果总是不对。…

【Java基础学习打卡09】JRE与JDK

目录 前言一、JRE二、JDK三、JDK、JRE和JVM关系总结 前言 本文将介绍JRE、JDK是什么&#xff0c;以及JDK、JRE和JVM关系三者之间的关系。 一、JRE JRE全称为Java Runtime Environment&#xff0c;是Java应用程序的运行时环境。JRE包括Java虚拟机&#xff08;JVM&#xff09;、…

车辆救援道路救援预约汽修托运小程序

道路救援&#xff1a;指汽车道路紧急救援&#xff0c;为故障车主提供包括诸如&#xff1a;拖吊、换水、充电、换胎、送油以及现场小修等服务(Road-Side Service)&#xff1b; 同时也指交通事故道路救援&#xff0c;包括伤员救治、道路疏导等。 随着我国巨大的汽车拥有量&…

基础篇:新手使用vs code新建go项目(从0开始到运行)

学习新语言&#xff0c;搭建新环境。在网上找了一些教程&#xff0c;感觉还是写一个比较详细的方便以后自己使用。其实vs code没有新建项目这个功能&#xff0c;具体怎么运行go语言的项目请看下文。 一、下载GO安装包 1.点击go安装包下载链接下载相应的版本&#xff08;本次下…

了解 Dockerfile 和搭建 Docker 私有仓库:让容器化部署变得更简单

目录 1、Dockerfile 1.1什么是Dockerfile 1.2常用命令 1.3使用脚本创建镜像 2、Docker私有仓库 2.1私有仓库介绍&#xff1a; 2.2私有仓库搭建与配置 2.3上传镜像到私有仓库&#xff1a; 1、Dockerfile 1.1什么是Dockerfile Dockerfile是由一些列命令和参数构成的脚本…

《网络安全0-100》安全事件案例

网络安全事件案例分析 2017年Equifax数据泄露事件 Equifax是美国一家信用评级机构&#xff0c;2017年9月&#xff0c;该公司披露发生了一起重大的数据泄露事件&#xff0c;涉及1.43亿美国人的个人信息&#xff0c;包括姓名、出生日期、社会安全号码等敏感信息。经过调查&#…

【数据分析】如何使用docker部署程序并移植(算法、接口)

原文作者&#xff1a;我辈李想 版权声明&#xff1a;文章原创&#xff0c;转载时请务必加上原文超链接、作者信息和本声明。 文章目录 前言一、Docker的基本使用1.安装Docker2.列出本地镜像3.获取镜像,创建本地ubuntu:13.10镜像4.查找镜像5.删除本地镜像6.创建自定义镜像7.镜像…

第一章 基础算法(二)——高精度,前缀和与差分

文章目录 高精度运算高精度加法高精度减法高精度乘法高精度除法 前缀和二维前缀和 差分二维差分 高精度练习题791. 高精度加法792. 高精度减法793. 高精度乘法794. 高精度除法 前缀和练习题795. 前缀和796. 子矩阵的和 差分练习题797. 差分798. 差分矩阵 高精度运算 两个大数做…

【Unity Shader】从入门到感慨(2)用C#画一个立方体

文章目录 一、构成一个立方需要多少个顶点?二、定义三角面的索引数组:三、定义UV坐标数组:四、最后构建Mesh:五、完整代码:一、构成一个立方需要多少个顶点? 这个问题是面试经常被问到的题。如上图,我们知道在几何中立方体有6个面,8个顶点。但在图形学中,顶点指的是模…

翻过那座山——Gitlab流水线任务疑难之编译有子模块的项目指南

&#x1f4e2;欢迎点赞 &#xff1a;&#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff0c;赐人玫瑰&#xff0c;手留余香&#xff01;&#x1f4e2;本文作者&#xff1a;由webmote 原创&#x1f4e2;作者格言&#xff1a;新的征程&#xff0c;我们面对的不是…

QT入门基础知识

什么是QT QT是一个跨平台的C图像用户界面应用程序框架QT在1991年由奇趣科技开发QT的优点 跨平台,几乎支持所有平台接口简单&#xff0c;容易上手一定程度上简化了内存回收机制有很好的社区氛围可以进行嵌入式开发 QWidget QT注意事项 命名规范 类名 首字母大写&#xff0c;单…

golang vscode环境报错gopls was not able to find modules in your workspace的解决方式

目录 错误提示 分析 解决方式 方法一&#xff1a;将workspace与项目路径保持一致 方案二&#xff1a;使用go work指明纳入工作区的的module 总结 错误提示 golang从老版本升级到go1.20.5后打开vscode&#xff0c;发现代码不能自动补全了&#xff0c;而且vscode跳出一下的…