C++——探究引用

news2024/12/26 15:55:11

文章目录

  • 概述
  • 引用的概念
  • 引用特性
  • 引用的作用
    • **引用做参数**
    • **引用作为函数返回值**
  • 常引用
  • 引用的底层实现
  • 总结一下引用和指针的不同点

概述

本篇博客将讲述c++相对于c新增的一个重要的内容——引用,深入研究其语法细节以及其需要注意的一些要点。

引用的概念

竟然要学习这个新的语法,那么首先就要知道它的概念是什么,那么引用到底是什么呢?

引用不是新定义一个变量,而是给已经存在的变量取一个别名,编译器不会为引用变量开辟空间,它和它引用的变量公用一块空间
(引用就好像我们现实世界中给一个物或者人取外号是一样的道理)。

语法使用如下:
类型&引用变量名(对象名)= 引用实体

看下面代码:

#include<iostream>

int main()
{
	int a = 10;
	int& ra = a;
	printf("%p\n", &a);
	printf("%p\n", &ra);
	return 0;
}

该段代码运行结果如下:
在这里插入图片描述
可以看到,这里raa两个变量的地址是相同的,也就是说他们两个指向的是同一块空间。

这里需要注意的是:引用类型必须和引用实体是同种类型,至于原因会在下面解释常引用时讲解。

引用特性

  1. 引用在定义时必须初始化
    例如:
    int& ra;
    是不能编译通过的,这也是引用和指针的差别之一
  2. 一个变量可以有多个别名
int a = 10;
int& ra = a;
int& raa = a;

这段代码是成立的,在现实世界中,这就类似于一个人可以有多个外号。
3. 引用一旦引用一个实体,再不能引用其他实体。

关于这一点,我们可以看看下面一个例子:

#include<iostream>
using namespace std;
int main()
{
	int a = 10;
	int b = 20;
	int& ra = a;
	ra = b;
	cout << ra << " " << a << endl;
	printf("%p\n%p\n%p\n", &a, &ra, &b);
	//C++兼容C语言
	return 0;
}

大家可以先猜猜这段代码运行的结果是什么?

是的,a和ra的值都被改变了,但是看看下a和ra的地址以及b的地址,我们发现ra依然是a的别名,因此,这里ra仍然是指向a,相当于是把b的值给了a,而并没有发生指向的改变

这是引用和指针的第二大特点: 引用不能改变指向的对象!

那么说了这么多,引用到底有什么作用呢?接下来就来看看引用的作用

引用的作用

其实引用的作用更多是用在形参和实参的连接上的。

引用做参数

回忆一下c语言阶段想让两个变量交换值我们会用什么函数。

//C语言的写法
void swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
} 
//函数调用时:
sawp(&a, &b);

那么当我们学习了引用之后,我们就可以使用引用来进行这个操作:

void swap(int& a, int& b)
{
	int tmp = a;
	a = b;
	b = tmp;
}
//函数调用时:
swap(a, b);

怎么样,是不是感觉引用方便了许多呢?

那么这里的原理是什么呢?
简单来说,就是这里的形参是实参的别名,也就是说他们都是指向同一块内存空间的变量,因此可以直接进行操作。

引用做参数时的另一个特点是:可以减少拷贝提升效率。

其实这一点指针也能做到,就是在传递一个很大的结构体的时候,如果我们直接使用穿值调用,将会消耗很大的开销,而如果是用传址调用或者传递引用调用,就会大大提升效率,并且相比起来,传引用更加方便。

引用作为函数返回值

我们就拿c语言实现顺序表数据结构的代码进行一下对比:

如果还不了解顺序表数据结构是什么的可以看一下下面csdn上一位大佬的博客:
C语言实现顺序表(数据结构)

现在假设我要修改顺序表指定位置元素的值,用c语言的方式该如何实现呢?

void SLmodify(SeqList*sl, int pos, int x)
{
	sl->a[pos] = x;
}
int SLfind(SeqList sl, int pos)
{
	return sl->a[pos];
}
int main()
{
	SeqList ls;
	//...
	//往顺序表内添加值
	
	//修改位置为5的元素,让其变成2倍
	int e = SLfind(&ls, 5);
	e+= 5;
	SLmodify(&ls, 5, e);
	return 0;
}

是不是相当繁琐?
而如果使用的是c++的引用,将会简单很多。

inr& SLat(SeqList* ls, int pos)
{
	return ls->a[pos];
}

int main()
{
	SeqList ls;
	//...
	
	//同样对5位置的值*2
	SLat(&ls,5)*=2;
	//一步到位!
}

那么,原理是什么呢?

当引用作为返回值时,返回的相当于是你返回变量的别名,也就是说返回的仍然是那个对象,而不是其拷贝

从而我们就可以总结出引用左返回值的两大特点:

  1. 能够减少拷贝提高效率
  2. 能够修改返回值+获取返回值

注意:
由于引用的特殊性,我们不能返回函数内的局部变量,这是由于函数内的局部变量在出函数后就直接销毁,虽然编译器不会报错,但是有可能得到的值不是我们想要的值。

请谨慎使用引用做返回值!!!


最后,我们来讲讲常引用是什么?

常引用

其实就是对引用变量前加上const修饰符,代表这个引用变量的值不能修改。

int main()
{
	 const int a = 10; 
	 //int& ra = a;           // 该语句编译时会出错,a为常量 
	 const int& ra = a;
	 // int& b = 10;         // 该语句编译时会出错,b为常量 
	 const int& b = 10; 
	 double d = 12.34; 
	 //int& rd = d;          // 该语句编译时会出错,类型不同 
	 const int& rd = d;
}

这里编译不同过的原因都是一个:权限放大

引用变量有一个问题就是只能平移权限或者缩小权限,而不能放大权限。
例如:int类型的右值不能赋给一个int&类型的引用变量。
其实很好理解为什么不行,如果能够进行这种操作,那么就有可能通过该引用变量来修改右值,这是不可以的!

那么这里就有一个有意思的点了,看上面的最后一个例子:

	double d = 12.34; 
	//int& rd = d;          // 该语句编译时会出错,类型不同 
	const int& rd = d;

有人可能会有疑惑,这里d并不是右值啊,为什么第二行代码不能编译通过呢?

这里就又涉及到一个知识点了,c++将一个与变量类型不同的另一个变量赋值给其的过程会先生成临时变量,而这里的临时变量是一个右值,因此编译不通过。
这也是为什么引用类型必须和引用实体是同种类型的原因,就是为了避免权限放大的问题!

引用的底层实现

看下面一段代码:

int main()
{
	int a = 10;
	int* pa = &a;

	int b = 10;
	int& rb = b;

	return 0;
}

我们利用反汇编的功能查看转换后的汇编后的代码:

在这里插入图片描述

可以看到,引用和指针的底层实现是一模一样的,引用其实是对指针使用的一种封装。

总结一下引用和指针的不同点

下面的不同点建议了解,而不是死记硬背!

  1. 引用概念上定义一个变量的别名,指针存储一个变量地址。 (底层实现相同)
  2. 引用在定义时必须初始化,指针没有要求
  3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
  4. 没有NULL引用,但有NULL指针
  5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占 4个字节)
  6. 引用自加(++)即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
  7. 多级指针,但是没有多级引用
  8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
  9. 引用比指针使用起来相对更安全(不会出现空引用的情况)

以上便是关于c++引用的解释,如果博主哪里写的有问题或者大伙有疑惑还请在评论区指出😁

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Baumer工业相机堡盟工业相机如何联合BGAPISDK和佳能EF变焦镜头实现相机的自动变焦(C++)

Baumer工业相机堡盟工业相机如何联合BGAPISDK和佳能EF变焦镜头实现相机的自动变焦&#xff08;C&#xff09; Baumer工业相机Baumer工业相机BGAPISDK中控制变焦镜头的技术背景代码案例分享第一步&#xff1a;开启相机自动调焦功能模块第二步&#xff1a;控制自动变焦镜头电机的…

Java+SSM+Mysql在线图书超市

课题背景及意义 随着现代网络技术发展&#xff0c;对于在线图书超市的设计现在正处于发展的阶段&#xff0c;所以对的要求也是比较严格的&#xff0c;要从系统的功能和用户实际需求来进行对系统制定开发的发展方式&#xff0c;依靠网络技术的的快速发展和现代通讯技术的结合为…

C++11: lambda、包装器、模板参数包和线程库

lambda C98中的一个例子 在C98中&#xff0c;如果想要对一个数据集合中的元素进行排序&#xff0c;可以使用std::sort方法。 int main() {int array[] { 4,1,8,5,3,7,0,9,2,6 };// 默认按照小于比较&#xff0c;排出来结果是升序std::sort(array, array sizeof(array) / s…

socket.io 解决浏览器兼容性(WebSocket)

在上一篇讲了 npm 上最流行的 WebSocket 库之一的 ws 库&#xff0c;那么本篇就来讲另外一个&#xff0c;就是 socket.io 库&#xff0c;socket.io 其实是一个兼容方案&#xff0c;当浏览器不支持 H5 的情况下就不能够使用上一篇内容讲的 WebSocket &#xff0c;只能采用其他的…

【数据统计】— 极大似然估计 MLE、最大后验估计 MAP、贝叶斯估计

【数据统计】— 极大似然估计 MLE、最大后验估计 MAP、贝叶斯估计 极大似然估计、最大后验概率估计(MAP)&#xff0c;贝叶斯估计极大似然估计(Maximum Likelihood Estimate&#xff0c;MLE)MLE目标例子: 扔硬币极大似然估计—高斯分布的参数 矩估计 vs LSE vs MLE贝叶斯公式&am…

JavaScript函数基础

●我们代码里面所说的函数和我们上学的时候学习的什么三角函数、二次函数之类的不是一个东西 函数的概念 ●对于 js 来说&#xff0c;函数就是把任意一段代码放在一个 盒子 里面 ●在我想要让这段代码执行的时候&#xff0c;直接执行这个 盒子 里面的代码就行 ●先看一段代码 …

[golang gin框架] 25.Gin 商城项目-配置清除缓存以及前台列表页面数据渲染公共数据

配置清除缓存 当进入前台首页时,会缓存对应的商品相关数据,这时,如果后台修改了商品的相关数据,缓存中的对应数据并没有随之发生改变,这时就需要需改对应的缓存数据,这里有两种方法: 方法一 在管理后台操作直接清除缓存中的所有数据,当再次访问前台首页时,就会先从数据库中获取…

巧用千寻位置GNSS软件|直线放样有技巧

日常测量作业中&#xff0c;直线放样是对设计好的直线进行放样&#xff0c;其中包括直线的里程&#xff0c;左右偏距及设计直线范围内的高程控制。本文将介绍如何运用千寻位置GNSS软件完成日常的直线放样。 点击【测量】->【直线放样】->【直线库】&#xff0c;选择一条直…

Python数组仿射变换

文章目录 仿射变换坐标变换的逻辑scipy实现 仿射变换 前面提到的平移、旋转以及缩放&#xff0c;都可以通过一个变换矩阵来实现&#xff0c;以二维空间中的变换矩阵为例&#xff0c;记点的坐标向量为 ( x , y , 1 ) (x,y,1) (x,y,1)&#xff0c;则平移矩阵可表示为 [ 1 0 T x …

数据结构和算法学习记录——二叉搜索树的插入操作、删除操作

目录 二叉搜索树的插入 思路图解 代码实现 要点 例题 二叉搜索树的删除 情况一 情况二 情况三 右子树的最小元素 左子树的最大元素 代码实现 二叉搜索树的插入 要进行二叉搜索树的插入&#xff0c; 关键点在于要找到元素应该插入到哪个位置&#xff0c;可以采…

Echarts数据可视化(下)

四、数据可视化项目实战 后台搭建 结合Vue开发图表组件 WebSocket实现数据推送 主题切换/页面合并/全屏切换 4.1 Koa2 学习介绍&#xff1a;koa2 - 简书 官网&#xff1a;Koa (koajs) -- 基于 Node.js 平台的下一代 web 开发框架 | Koajs 中文文档 4.1.1 Koa2 的介绍 基…

springboot+vue校园新闻网站(源码+说明文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的校园新闻网站。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 &#x1f495;&#x1f495;作者&#xff1a;风歌&a…

【数据结构】树及二叉树的概念

&#x1f61b;作者&#xff1a;日出等日落 &#x1f4d8; 专栏&#xff1a;数据结构 一次失败&#xff0c;只是证明我们成功的决心还够坚强。 ——博 维 目录 &#x1f384;树概念及结构: ✔树的概念: ✔树的相关概念 :​编辑 ✔树的…

【C++】-一文让你半只脚进入C++,还不进来看看??

作者&#xff1a;小树苗渴望变成参天大树 作者宣言&#xff1a;认真写好每一篇博客 作者gitee:gitee 如 果 你 喜 欢 作 者 的 文 章 &#xff0c;就 给 作 者 点 点 关 注 吧&#xff01; C入门 前言一、C关键字二、namespace和using关键字2.1namespace和using的使用2.2names…

WebStorm前端启动JetLinks 物联网基础平台(2.x)

目录 一、环境准备 二、下载源码 三、安装依赖 ​四、修改配置 五、启动项目 六、访问项目 一、环境准备 1.降级node版本为12.22.0 使用node版本管理器gnvm_苍穹之跃的博客-CSDN博客以管理员身份打开cmd&#xff0c;cd到node安装目录。https://blog.csdn.net/wenxingch…

联发科 2024届 IC实习笔试分析

说明 记录一下 4月19日晚&#xff0c;发哥的一场笔试。分享给需要的 IC 人。 岗位&#xff1a;数字IC设计验证&#xff08;安徽 合肥&#xff09; 转载要经本人同意&#xff01; 我的见解不一定都是准确的&#xff0c;欢迎评论区交流指正~~ 1、&#xff08;20分&#xff0…

滚动条如何设置样式和滚动条悬浮显示与隐藏

文章目录 一、滚动条如何设置样式1&#xff1a;滚动条的默认样式&#xff08;如下图&#xff09;1&#xff1a;html代码2&#xff1a;css代码3&#xff1a;效果图 2&#xff1a;CSS设置滚动条的属性&#xff08;重点&#xff09;3&#xff1a;设置滚动条的例子1&#xff1a;css…

java面经01-基础篇-排序算法、ArrayList、Iterator、LinkedList、HashMap、单例模式

文章目录 基础篇1. 二分查找2. 冒泡排序3. 选择排序4. 插入排序5. 希尔排序6. 快速排序7. ArrayList7.1 初始化:7.2 add扩容7.3 addAll扩容 8. Iterator8.1 ArrayList 源码分析8.2 CopyOnWriteArrayList 源码分析 9. LinkedList10. HashMap1&#xff09;基本数据结构2&#xff…

C#基础学习--LINQ(2

标准查询运算符 标准查询运算符由一系列API方法组成 序列指实现了Ienumerable<>接口的类&#xff0c;包括List<>,Dictionary<>,Stack<>,Array等 标准查询运算符的签名 扩展方法是公共的静态方法&#xff0c;尽管定义在一个类中&#xff0c;但目的是为…

python整活时间到——27行代码一键获取写真集~~~

嗨害大家好鸭&#xff01;我是爱摸鱼的芝士❤ 来吧&#xff0c;直接整活~ 先准备一下 首先咱们需要安装一下这两个第三方模块 requests >>> parsel >>> 不会安装的小伙伴&#xff0c;键盘按住winr 在弹出来的运行框 输入cmd 按确定&#xff0c;然后弹出…