C++入门——引用(2)

前言

上一节我们开始学习了C++,并且对C++有了初步的了解,这一节我们继续学习C++的基础,那么废话不多说,我们正式进入今天的学习

C++中的引用

1.1引用的概念

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

引用的语法如下:

	int a = 0;int& b = a;

第二行的代码的意思是:给已创建的变量a取一个别名为b,注意这里的&符号不是取地址的意思,而是引用的意思

当&符号放在一个类型和一个变量的中间位置才叫做引用,不然的话仍然起的是取地址的功能。

int main(void)
{int a = 0;int& b = a;cout << &a << endl;cout << &b << endl;return 0;
}

通过运行以上代码我们可以知道:引用并没有重新开辟一个空间,而是和原变量共用一个空间

我们对b变量执行++操作也相当于对a变量执行++操作,因为它们本质上就是同一个变量

引用的概念有点像我们在日常生活中给别人起的外号

学到这里可能有人会觉得引用没有什么用,只是给变量取了一个“外号”罢了,其实并不是这样的。我们在C语言中若是要完成交换两个不同变量中的数据内容时,我们创建swap函数应该要传入两个变量的地址,如果是传值调用则无法完成交换的功能,因为在出函数的时候临时的变量被销毁。但是有了引用就可以不用使用指针,引用传参修改的值可以直接影响到原来的参数

void swap(int& x1, int& x2)
{int tmp = x1;x1 = x2;x2 = tmp;
}int main(void)
{int x = 1;int y = 2;swap(x, y);cout << "x = " << x << endl;cout << "y = " << y << endl;return 0;
}

1.2引用的注意事项

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

 int a = 10;// int& ra;   // 该条语句编译时会出错

2. 一个变量可以有多个引用

int a = 0;
int& ra = a;
int& rra = a;

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

int a = 0;
int& b = a;
int x = 1;
b = x;
//这种情况下并不是把b变成x的引用,而是给b中赋值为1,也就是a改为1

4.引用的类型必须和引用实体的类型相同

1.3传引用返回

首先我们需要了解一个概念,叫做传值返回:

int Count()
{int n = 0;n++;//……return n;
}int main(void)
{int ret = Count();return 0;
}

在出函数的时候变量n已经被销毁了,所以函数并不是直接拿n作为返回值,此时编译器用以下两种方式返回

1.把n拷贝到一个寄存器中,让寄存器充当返回值,该情况适用于返回值比较小的情况

2.当n的取值比较大的的时候,会在Count函数和main函数之间的空隙里面压出一个空间,将值存入并将它作为一个返回值

随后我们来了解一下引用返回

int& Count()
{int n = 0;n++;//……return n;
}int main(void)
{int ret = Count();return 0;
}

传引用返回相当于返回的是值n的别名,这个概念看起来有些奇怪,因为原来的变量n已经被销毁了,而返回的值又是已经被销毁的变量的别名,所以有的人可能认为这个概念的性质和野指针差不多 。首先我们需要知道的是,当一个变量或者一个空间被销毁了,仍然可以返回这个已经被销毁的空间里面的变量的别名,因为空间被销毁并不是意味着这一块空间已经没有了。空间销毁的概念有点类似于我们在日常生活中的“退房”,我们销毁了的那块空间并不是不能使用了,而是收回了对那块空间的使用权。但是传引用返回是比较危险的,传引用返回一般都会报一个警告,返回来的值可能是正常值也有可能是一个随机值,这取决于函数栈帧销毁了以后原空间里面的内容会不会被重置为一个随机值

int& Count()
{int n = 0;n++;//……return n;
}int main(void)
{int& ret = Count();cout << ret << endl;cout << ret << endl;return 0;
}

我们来看一下这样的情况下为什么第二次打印的结果是一个随机值:

我们首先需要了解,cout是一个函数调用,调用的是一个运算符重载的函数,cout是ostream类型

我们在第一次调用函数的时候并不会出现任何的问题,而当我们第二次调用函数的时候,创建栈帧的区域还是在第一次调用所创建的区域,此时区域内原先的数据就已经被覆盖了,所以打印出来的是一个随机值

我们来举一个例子就能更好地理解这一点:

int& Add(int a, int b)
{int c = a + b;return c;
}int main(void)
{int& ret = Add(1, 2);Add(3, 4);cout << "Add(1,2) is :" << ret << endl;return 0;
}

因为本来执行Add操作计算出来的值是3,但是我们在第二次调用函数的时候算出来的值是7,第二次调用覆盖了第一次调用,所以此时去打印算出来的值是7

所以:如果函数返回时,出了函数作用域,如果返回对象还在(还没有还给系统),则可以使用引用返回,如果已经还给系统了,则必须使用传值返回

我们在C语言中要完成顺序表的第i个位置的数据和修改需要两个步骤:

struct Seqlist
{int* a;int size;int capacity;
};
//读取第i个位置的值
//修改第i个位置的值
int SLAT(struct Seqlist* ps, int i)
{assert(i < ps->size);//...return ps->a[i];
}int SLModify(struct Seqlist* ps, int i, int x)
{assert(i < ps->size);//...return ps->a[i] = x;
}

而在C++中使用引用返回就可以简化这一过程:

struct Seqlist
{int* a;int size;int capacity;
};
//读取&修改第i个位置的值
int& SLAT(struct Seqlist& ps, int i)
{assert(i < ps.size);//...return ps.a[i];
}
int main(void)
{struct Seqlist s;//...SLAT(s, i) = 1;cout << SLAT(s, i) << endl;return 0;
}

当出了作用域时,ps.a[i] 仍然存在,所以可以直接对其修改,修改过后的值仍然存在

怎么解释这个现象呢?

因为传值返回出函数的时候返回的是一份临时的拷贝,是不可以直接修改的

而传引用返回返回的是别名,是可以被修改的

传值、传引用效率比较

以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效率是非常低下的,尤其是当参数或者返回值类型非常大时,效率就更低。如果是传引用的话就能够提高效率,当然,传指针也可以

#include <time.h>
struct A { int a[10000]; };
void TestFunc1(A a) {}
void TestFunc2(A& a) {}
void TestRefAndValue()
{A a;// 以值作为函数参数size_t begin1 = clock();for (size_t i = 0; i < 10000; ++i)TestFunc1(a);size_t end1 = clock();// 以引用作为函数参数size_t begin2 = clock();for (size_t i = 0; i < 10000; ++i)TestFunc2(a);size_t end2 = clock();// 分别计算两个函数运行结束后的时间cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}int main(void)
{TestRefAndValue();return 0;
}

由此我们可以看出:传引用返回比传值返回的效率要高

那么在什么情况下适合使用传引用返回呢?

1.返回的是一个全局对象

2.返回的是一个静态对象

3.在堆上动态申请的对象

因为这三个变量出了作用域仍然存在,使用传引用返回就可以在确保没有错误的情况下提高效率

此时我们就可以总结一下:

传引用传参的优势(在任何的情况下都可以

1.提高效率

2.输出型参数(形参的修改可以影响实参)

传引用返回的优势(出了函数作用域对象还在才可以使用

1.提高效率

2.修改返回的对象

1.4常引用

int main(void)
{const int a = 0;int& b = a;return 0;
}

在这种情况下,编译是无法通过的,因为这是一种权限的放大。在引用的过程中权限是可以平移和缩小的,但是不能被放大

权限的平移:

const int a = 0;
//权限的平移
const int& c = a;

权限的缩小:

int x = 0;
//权限的缩小
const int& y = x;

注意:赋值是不受权限的影响的,因为赋值是一种拷贝,b的修改不影响a

const int a = 0;
//赋值
int b = a;

我们再来看一个有趣的现象:

我们这样写出的代码编译会失败:

int i = 0;
double& d = i;

而这样写编译就不会报错:

int i = 0;
const double& d = i;

第一种情况其实是一种权限的放大,临时变量具有常性

而第二种情况我们加入了const,此时就没有权限的放大了

1.5引用与指针的区别

我们通过之前的学习可以知道:指针在创建的时候会在内存中开辟空间,而在语法上来看引用是不会额外的开辟空间的(其实在底层实现上引用也会占用空间,但是在语法的层面上不会占用空间)

引用和指针的不同点:

1.引用是定义一个变量的别名,而指针是存储一个变量的地址

2.引用在定义的时候必须要初始化,而指针没有要求

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

4.没有NULL引用,但是有NULL指针

5.sizeof的含义不同:引用结果是引用类型的大小,但是指针始终是地址空间所占用的字节数(4或者8)

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

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

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

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

结尾

引用是C++中的一个重要的概念,我们需要加深对引用的理解,有助于我们更好的学习C++,那么本节的内容到此就结束了,谢谢您的浏览!!!

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

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

相关文章

uniapp小程序:大盒子包裹小盒子但是都有点击事件该如何区分?

在开发过程中我们会遇到这种情况&#xff0c;一个大盒子中包裹这一个小盒子&#xff0c;两个盒子都有点击事件&#xff0c;例如&#xff1a; 这个时候如果点击评价有可能会点击到它所在的大盒子&#xff0c;如果使用css中的z-index设置层级的话如果页面的盒子多的话会混乱&…

Spring解决泛型擦除的思路不错,现在它是我的了。

你好呀&#xff0c;我是浮生。 Spring 的事件监听机制&#xff0c;不知道你有没有用过&#xff0c;实际开发过程中用来进行代码解耦简直不要太爽。 但是我最近碰到了一个涉及到泛型的场景&#xff0c;常规套路下&#xff0c;在这个场景中使用该机制看起来会很傻&#xff0c;但…

15、FreeRTOS 软件定时器

文章目录 一、什么是定时器?1.1 定时器的理解1.2 软件定时器的特性 二、 软件定时器的上下文2.1 守护任务2.2 守护任务的调度2.3 回调函数 三、软件定时器的函数3.1 创建3.2 删除3.3 启动/停止3.5 修改周期3.6 定时器ID 四、案例4.1 一般使用4.2 消除抖动 一、什么是定时器? …

Midjourney Imagine API 申请及使用

Midjourney Imagine API 申请及使用 申请流程 要使用 Midjourney Imagine API&#xff0c;首先可以到 Midjourney Imagine API 页面点击「Acquire」按钮&#xff0c;获取请求所需要的凭证&#xff1a; 如果你尚未登录或注册&#xff0c;会自动跳转到登录页面邀请您来注册和登…

语音转文字服务的调用接口

语音转文字&#xff08;Speech-to-Text&#xff0c;STT&#xff09;技术允许将口语化的语音转换成书面文字。以下是一些提供语音转文字服务的调用接口及其特点。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1.讯飞开放平台语音转写…

[猫头虎分享21天微信小程序基础入门教程]第1天:微信小程序概述与开发环境搭建教程

第1天&#xff1a;微信小程序概述与开发环境搭建 &#x1f63a; 文章目录 第1天&#xff1a;微信小程序概述与开发环境搭建 &#x1f63a;自我介绍微信小程序概述特点 开发环境搭建步骤1: 注册微信小程序账号步骤2: 安装开发者工具步骤3: 熟悉开发者工具界面 今日学习总结小测试…

炒股开户佣金最低万1和万0.854,融资融券现在利率最低4.0%~5%

​​炒股开户佣金一般是万1和万0.854&#xff0c;万0.854有一定的资金量要求&#xff0c;高于万1的是可以申请降低的。 开户万1佣金和万0.854佣金只需要联系证券公司客户经理协商就行。 开户流程&#xff1a; 1、向客户经理索要开户链接或者扫描二维码、进入申请页面&#x…

本地搭建各大直播平台录屏服务结合内网穿透工具实现远程管理录屏任务

文章目录 1. Bililive-go与套件下载1.1 获取ffmpeg1.2 获取Bililive-go1.3 配置套件 2. 本地运行测试3. 录屏设置演示4. 内网穿透工具下载安装5. 配置Bililive-go公网地址6. 配置固定公网地址 本文主要介绍如何在Windows系统电脑本地部署直播录屏利器Bililive-go&#xff0c;并…

Nachi那智不二越机器人维修技术合集

一、Nachi机械手维护基础知识 1. 定期检查&#xff1a;定期检查机器人的各个部件&#xff0c;如机械手伺服电机、机器人减速器、机械臂传感器等&#xff0c;确保其运行正常。 2. 清洁与润滑&#xff1a;定期清洁Nachi工业机器人表面和内部&#xff0c;并使用合适的润滑油进行润…

VRRP协议-负载分担配置【分别在路由器与交换机上配置】

VRRP在路由器与交换机上的不同配置 一、使用路由器实现负载分担二、使用交换机实现负载分担一、使用路由器实现负载分担 使用R1与R2两台设备分别进行VRRP备份组 VRRP备份组1,虚拟pc1的网关地址10.1.1.254 VRRP备份组2,虚拟pc2的网关地址10.1.1.253 ①备份组1的vrid=1,vrip=…

vue3中使用cherry-markdown

附cherry-markdown官网及api使用示例 官网:https://github.com/Tencent/cherry-markdown/blob/main/README.CN.md api:Cherry Markdown API 考虑到复用性,我在插件的基础上做了二次封装,步骤如下: 1.下载 (一定要指定版本0.8.22,否则会报错: [vitel Internal server e…

初识指针(5)<C语言>

前言 在前几篇文章中&#xff0c;已经介绍了指针一些基本概念、用途和一些不同类型的指针&#xff0c;下文将介绍某些指针类型的运用。本文主要介绍函数指针数组、转移表&#xff08;函数指针的用途&#xff09;、回调函数、qsort使用举例等。 函数指针数组 函数指针数组即每个…

深度学习知识点全面总结

ChatGPT 深度学习是一种使用神经网络来模拟人脑处理数据和创建模式的机器学习方法。下面是深度学习的一些主要知识点的总结&#xff1a; 1. 神经网络基础&#xff1a; - 神经元&#xff1a;基本的计算单元&#xff0c;模拟人脑神经元。 - 激活函数&#xff1a;用于增加神…

【CSP CCF记录】数组推导

题目 过程 思路 每次输入一个Bi即可确定一个Ai值&#xff0c;用temp记录1~B[i-1]&#xff0c;的最大值分为两种情况&#xff1a; 当temp不等于Bi时&#xff0c;则说明Bi值之前未出现过&#xff0c;Ai必须等于Bi才能满足Bi是Ai前缀最大的定义。当temp等于Bi时&#xff0c;则说…

SpringAMQP-消息转换器

这边发送消息接收消息默认是jdk的序列化方式&#xff0c;发送到服务器是以字节码的形式&#xff0c;我们看不懂也很占内存&#xff0c;所以我们要手动设置一下 我这边设置成json的序列化方式&#xff0c;注意发送方和接收方的序列化方式要保持一致 不然回报错。 引入依赖&#…

重磅推出:135届广交会采购商名录,囊括28个行业数据!

5.5日&#xff0c;第135届中国进出口商品交易会&#xff08;简称广交会&#xff09;在广州圆满闭幕&#xff0c;这一全球贸易盛典再次展现了中国制造的卓越实力和文化魅力&#xff0c;成就斐然&#xff0c;吸引了全球目光。 本届广交会线下出口成交额达247亿美元&#xff0c;对…

项目-坦克大战-让坦克动起来

为什么写这个项目 好玩涉及到java各个方面的技术 1&#xff0c;java面向对象 2&#xff0c;多线程 3&#xff0c;文件i/o操作 4&#xff0c;数据库巩固知识 java绘图坐标体系 坐标体系-介绍 坐标体系-像素 计算机在屏幕上显示的内容都是由屏幕上的每一个像素组成的像素是一…

力扣HOT100 - 70. 爬楼梯

解题思路&#xff1a; 动态规划 注意 if 判断和 for 循环 class Solution {public int climbStairs(int n) {if (n < 2) return n;int[] dp new int[n 1];dp[1] 1;dp[2] 2;for (int i 3; i < n; i) {dp[i] dp[i - 1] dp[i - 2];}return dp[n];} }

品牌设计理念和logo设计方法

一 品牌设计的目的 设计是为了传播&#xff0c;让传播速度更快&#xff0c;传播效率更高&#xff0c;减少宣传成本 二 什么是好的品牌设计 好的设计是为了让消费者更容易看懂、记住的设计&#xff0c; 从而辅助传播&#xff0c; 即 看得懂、记得住。 1 看得懂 就是让别人看懂…

树莓派|采集视频并实时显示画面

1、使用SSH远程连接到树莓派 2、新建存放代码的目录 mkdir /home/pi/my_code_directory 3、进入存放代码的目录 cd /home/pi/my_code_directory 4、新建py文件 nano cv2test.py 5、输入代码 import cv2# 打开摄像头 cap cv2.VideoCapture(0)while True:# 读取视频帧ret…