21天学会C++:Day5----引用

news2025/2/24 13:16:55

· CSDN的uu们,大家好。这里是C++入门的第五讲。
· 座右铭:前路坎坷,披荆斩棘,扶摇直上。
· 博客主页: @姬如祎
· 收录专栏:C++专题

 

目录

 1. 知识引入

 2. 引用的特性

 2.1 引用在定义时必须初始化

2.2 一个变量可以有多个引用

3. 引用一旦引用一个实体,再不能引用其他实体

3. 常引用

​编辑

4. 引用的使用场景

4.1 做参数

4.2 做返回值

5. 引用的底层实现

6. 引用与指针的区别


 1. 知识引入

想必大家都还记得在用C语言实现单链表的时候的痛苦吧,什么二级指针,太难了!我们来看看当时的代码:

typedef struct ListNode
{
	struct ListNode* next;
	int val;
} ListNode;

void ListPushHead(ListNode** pphead)
{
	//代码实现省略
}

int main()
{
	struct ListNode* phead = NULL;
	ListPushHead(&phead);
	return 0;
}

因为形参只是实参的临时拷贝,形参的改变不影响实参。因此想要改变 phead 就必须要传 phead的地址过去。phead又是指针,所以就要传二级指针。真令人头大!

于是C++引入了新语法:引用。我们来看看引用的定义。

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。(这是在引用理解层面的定义,至于引用到底开不开空间,后面会讲解的)
 比如:李逵,在家称为"铁牛",江湖上人称"黑旋风"。

语法:

类型&  引用变量名(对象名)  =  引用实体;

注意:引用类型必须和引用实体是同种类型的。

我们来看看有了C++的引用,上面的代码可以怎么写呢?

你可以将形参定义为这个结构体指针的引用,因为引用是变量的别名,结构体指针的引用那么就是这个结构体指针的别名,通过改变这个别名指向的内容就能改变结构体指针指向的内容。

typedef struct ListNode
{
	struct ListNode* next;
	int val;
} ListNode;

void ListPushHead(ListNode*& pphead)
{
	//代码实现省略
}

int main()
{
	struct ListNode* phead = NULL;
	ListPushHead(phead);
	return 0;
}

 或者你还可以这么写:

将结构体 typedef 为 *ListNode,那么 ListNode 就是结构体指针,这样在形参的书写上会更加简洁。但是理解起来就有一点困难了。

typedef struct ListNode
{
    struct ListNode* next;
    int val;
} *ListNode;

void ListPushHead(ListNode& pphead)
{
    //代码实现省略
}

int main()
{
    struct ListNode* phead = NULL;
    ListPushHead(phead);
    return 0;
}

 2. 引用的特性

我们再来看看引用的定义:

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

在理解引用时,你可以直接在脑海中呈现一张图:

有了这个图,我们来理解引用的特性。 

 2.1 引用在定义时必须初始化

我们可以看到下面的代码是编译失败的,原因就是引用在定义的时候没有初始化。至于为什么必须在定义的时候初始化,后面会讲解。

2.2 一个变量可以有多个引用

这个很好理解,一个变量当然可以有多个别名啦!就像一个人,他可能会有乳名,小名,大名等等。同样地,你可以在脑海中呈现那个图:

最后那条语句,我们让定义了一个变量 b 的引用,实际上 d 也指向 a 所指向的空间,因为 b 是 a 的引用嘛。既然多个变量指向可同一块空间那么,我们改变其中一个变量的值,其他的变量的值肯定也会发生相应的变化。上面的图可以很好地帮助大家理解。

int main()
{
	int a = 20;
	int& b = a;
	int& c = a;
	int& d = b;
	d = 30;
	cout << "a: " << a << endl;
	cout << "b: " << b << endl;
	cout << "c: " << c << endl;
	cout << "d: " << d << endl;

	return 0;
}

3. 引用一旦引用一个实体,再不能引用其他实体

这里可以先尝试记忆,原因后面才会提到。

我们来看下面的代码,c = b 看上去好像是让 c 指向 b 这个变量,但实际上只是将 b 的值赋值给了 c ,并不是改变了引用的实体。我们可以通过打印值来证明。

int main()
{
	int a = 20;
	int b = 30;
	int& c = a;
	c = b;

	return 0;
}

 我们可以看到将 b 赋值给 c ,改变了 c 的值,也改变了 a 的值,这说明 c 还是变量 a 的引用,c = b 只是将 b 的值赋值给 c。

3. 常引用

我们来看上面的代码为什么会报错:const 修饰了变量 a,代表 a 指向的空间的内容不允许被改变。我们将变量 a 取别名为 ra,这个时候我们的操作权限就被放大了。因为引用变量没有加任何的限定符,表示通过引用变量 ra 能够修改 ra 指向的内容,而 ra 又是 a 的别名,则可推出 通过 ra能够改变 a 指向的内容。这就会与 const int a = 20;相冲突。

因此对于常量的引用,我们必须加上 const 限定符。不能让操作权限放大。

int main()
{
	const int a = 20;
	const int& ra = a; //加上 const 限定符,进制权限的放大
	return 0;
}

有人就可能会说了,既然不允许权限的放大,那允不允许权限的缩小呢?当然是可以的啦。

int main()
{
	int a = 20;
	const int& ra = a;
	return 0;
}

 通过 const 限定符使得 ra 的权限缩小,只能读 ,不能修改。

4. 引用的使用场景

4.1 做参数

在知识引用里面就是引用做参数的例子呀!

下面的知识点会涉及函数栈帧的相关知识,不懂的老铁可以先瞅瞅:详解函数栈帧的创建和销毁。

我们知道在函数栈帧创建的过程中,实参向形参的传递会拷贝实参,但是拷贝实参必然会有时间的消耗。但是引用就比较特殊啦,因为引用是变量的别名,我们在理解的层次上,可以认为引用并不会开辟空间,所以在传递参数的时候,效率就会比不传引用高。下面的代码会一定程度上证明只一点:

struct A { int a[10000]; }; 

void Func1(A a) {}

void Func2(A& a) {}


int main()
{
	A a;
	int begin1 = clock();
	for (int i = 0; i < 100000; i++)
		Func1(a);
	int end1 = clock();

	int begin2 = clock();
	for (int i = 0; i < 100000; i++)
		Func2(a);
	int end2 = clock();

	cout << "传值花费的时间:" << end1 - begin1 << endl;
	cout << "传引用花费的时间:" << end2 - begin2 << endl;

	return 0;
}

4.2 做返回值

做返回值就和普通的变量没什么大的区别,但有一点值得注意:因为引用是变量的别名,将引用作为函数的返回值,我们必须确保该变量不能在这个函数调用完毕后就被销毁了,即是:不可返回局部变量的引用。

大家能猜到下面的代码的输出结果吗?(IDE:VS2019)

int Func(int a, int b)
{
	int d = 0;
	d = a + b;
	return d;
}

int& Add(int a, int b)
{
	int c = 0;
	c = a + b;
	return c;
}

int main()
{
	int& a = Add(10, 20);
	cout << a << endl;
	Func(20, 30);
	cout << a << endl;
	return 0;
}

 下面来解释原因:

 我们来看看Linux下的g++编辑器的结果:segmentation fault,段错误,非法访问内存。

 切记:不可返回局部变量的引用。

还有一点就是:在返回值上引用也是不需要拷贝的,效率也是比直接返回值高好吧!这里就不再写代码演示了,代码和上面测试传参的相差不大。

5. 引用的底层实现

我们可以在调试的过程中查看汇编代码来观察引用的底层实现:

 我们看到引用的底层实现还是C语言的指针啊!那么我们就可以解释为什么引用定义出来就必须初始化,引用的实体不能改变了!原因就是这个指针经过了 const 修饰,类似于这样:

int* const reference

因为 cosnt 修饰所以我们必须在引用定义的时候初始化。因为 const 修饰所以我们不能更改引用的实体。

但是,我们在平时的应用时,将引用理解为别名,不开辟空间即可,当有人问你引用的底层实现时,你知道就行了!这样能简化理解的难度。

6. 引用与指针的区别

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

 上面的这些东东在理解的基础上记忆即可!切不可死记硬背。

好了,引用就差不多讲完了,有什么不正确的地方欢迎大家的指正。

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

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

相关文章

车间静电消除不掉?静电接地桩来帮忙!

静电接地桩的原理是通过将金属导体与地面相连&#xff0c;以便在设备运行时能够稳定地将静电荷自然地释放到地面中&#xff0c;从而保护人员和设备不受到静电的危害。 在工业生产中&#xff0c;静电容易在人体和物体表面积聚&#xff0c;如果不及时地排放处理会对人员和设备造…

【算法】简单讲解如何使用两个栈实现一个队列

文章目录 什么是栈和队列&#xff1f;设计思路代码实现 什么是栈和队列&#xff1f; 栈和队列其实大家基本都知道是什么&#xff0c;或者说&#xff0c;最基本的&#xff0c;他们的特性我们是知道的。 栈是一种FILO先进后出的数据结构&#xff0c;队列是一种FIFO先进先出的数据…

SRS流媒体服务器 ---- st-thread框架

1.使用st-thread 我们用一个简单的demo研究一下st框架。 #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include "st.h"static void *_thread(void *arg) {printf("thread: %lu\n", pthread_self());return NULL; }i…

实例6.1 六度空间

“六度空间”理论又称作“六度分隔&#xff08;Six Degrees of Separation&#xff09;”理论。这个理论可以通俗地阐述为&#xff1a;“你和任何一个陌生人之间所间隔的人不会超过六个&#xff0c;也就是说&#xff0c;最多通过五个人你就能够认识任何一个陌生人。”如图1所示…

MySql MVCC 详解

注意以下操作都是以InnoDB引擎为操作基准。 一&#xff0c;前置知识准备 1&#xff0c;MVCC简介 MVCC 是多版本并发控制&#xff08;Multiversion Concurrency Control&#xff09;的缩写。它是一种数据库事务管理技术&#xff0c;用于解决并发访问数据库的问题。MVCC 通过创…

ROS学习——Gazebo中搭建模型并显示

一、打开gazebo搭建模型 gazebo 在gazebo界面左上角点击“Edit”——>"Building Editor"进入下图的模型搭建界面。可以自己利用左边的材料搭建模型。 点击墙壁之类的物品&#xff0c;右键&#xff0c;点击“Open Wall Inspector”按钮&#xff0c;就会出现可以调…

jmeter做接口压力测试_jmeter接口性能测试

jmeter是apache公司基于java开发的一款开源压力测试工具&#xff0c;体积小&#xff0c;功能全&#xff0c;使用方便&#xff0c;是一个比较轻量级的测试工具&#xff0c;使用起来非常简单。因为jmeter是java开发的&#xff0c;所以运行的时候必须先要安装jdk才可以。jmeter是免…

第11届蓝桥杯Scratch省赛真题集锦

编程题 第 1 题 问答题 对对碰 题目说明 编程实现 对对碰 两两相同的一共四张扣下的纸牌&#xff0c;每次先后翻开两张。如果两张一样就消失&#xff0c;如果两张不一样就重新扣下。当舞台上所有纸牌都消失&#xff0c;就过关了 .1)创建四个经牌角色&#xff0c;每张纸牌…

linuxOPS基础_Linux文件管理

Linux下文件命名规则 可以使用哪些字符&#xff1f; 理论上除了字符“/”之外&#xff0c;所有的字符都可以使用&#xff0c;但是要注意&#xff0c;在目录名或文件名中&#xff0c;不建议使用某些特殊字符&#xff0c;例如&#xff0c; <、>、&#xff1f;、* 等&…

表单重复提交:

1. 表单重复提交原因 当用户提交完请求&#xff0c;浏览器会记录最后一次请求的全部信息。用户按下功能键F5&#xff0c;就会发起浏览器记录的最后一次请求。如果最后一次请求为添加操作&#xff0c;那么此时刷新按钮就会再次提交数据&#xff0c;造成表单重复提交。 2. 表单…

Hive优化

Hive的本质是MapReduce&#xff0c;优化其实大部分是对mapreduce的优化 hive优化目标&#xff1a;①横向增加并发&#xff0c;②纵向减少依赖 //开启mapjoin&#xff0c;默认为 true • set hive.auto.convert.join true; //开启map端数据聚合 • hive.map.aggrtrue&…

API的应用范围主要有哪些方面?

API&#xff08;Application Programming Interface&#xff09;即应用程序接口&#xff0c;它是一组规则和工具&#xff0c;通过 HTTP 协议将两个软件应用程序之间的通信连接起来。API 的设计可以使不同应用程序的数据和功能进行交互和共享&#xff0c;从而促进了各种应用程序…

对讲机在未来会有更好的发展吗?

对讲机经过几十年的发展&#xff0c;目前在很多领域都有着广泛的应用。那么在未来对讲机还会有更好的发展吗&#xff1f; 对讲机未来会有更好的发展吗 下面河南宝蓝小编根据目前的发展情况做一些猜想&#xff1a; 一、更高的频率范围 目前对讲机所使用的频率范围主要是在VHF…

Spring的作用域和生命周期

目录 1.Bean的作用域 2.Bean的作用域的分类 3.设置作用域 4.Spring的执行流程&#xff08;生命周期&#xff09; 5.Bean的生命周期 1.Bean的作用域 lombok &#xff08;dependency依赖&#xff09; 是为了解决代码的冗余&#xff08;比如说get和set方法&#xff09;那些构造…

平衡二叉树的插入,删除以及平衡调整。

一&#xff0c;平衡二叉树插入失衡情况及解决方案 由于各种的插入导致的不平衡&#xff0c;每次调整都是最小不平衡子树。 LL&#xff1a;由于在结点A的 左孩子的左子树 插入结点导致失衡。 右单旋&#xff1a;①将A的 左孩子B 向右上旋转 代替A成为根节点       ②将A结…

从零开始:使用低代码平台开发OA系统的教程

随着中小型企业持续拥抱数字化转型&#xff0c;对支持业务流程的定制软件应用程序的需求增加。而办公自动化(OA)系统是一个有助于自动执行重复性任务并简化工作流程的系统。按照传统的开发模式&#xff0c;开发OA系统可能既耗时又昂贵&#xff0c;需要经验丰富的开发人员从头开…

ESP32-IDF MQTT连接aws亚马逊云

ESP32-IDF MQTT连接aws亚马逊云 文章目录 ESP32-IDF MQTT连接aws亚马逊云1. 云端配置2. 设备端配置3. 总结 1. 云端配置 登录AWS&#xff0c;地址: https://aws.amazon.com/ 选择IOT core 服务 创建云端设备&#xff0c;点击连接一台设备 进行云端设备创建&#xff0c;按照流…

1020. 飞地的数量

1020. 飞地的数量 C代码&#xff1a;DFS void dfs (int** grid, int x, int y, int m, int n) {if (x < 0 || x > m || y < 0 || y > n || grid[x][y] 0) {return;}grid[x][y] 0;dfs(grid, x 1, y, m, n);dfs(grid, x - 1, y, m, n);dfs(grid, x, y 1, m, n);…

Activiti7学习笔记

Activiti7学习 工作流相关概念 工作流 工作流(Workflow)&#xff0c;就是通过计算机对业务流程自动化执行管理。它主要解决的是“使在多个参与者之间按照某种预定义的规则自动进行传递文档、信息或任务的过程&#xff0c;从而实现某个预期的业务目标&#xff0c;或者促使此目…

new bing 初体验:辅助看论文刚刚好

1. new bing使用条件 &#xff08;1&#xff09;安装Microsoft edge的dev版本 https://www.microsoft.com/zh-cn/edge/download?formMA13FJ &#xff08;2&#xff09;浏览器侧栏打开 Discover (3) 进入new bing 页面 侧栏展示 new bing 如果这一步&#xff0c;没有聊天功能…