初识C++ (三)

如果很迷茫的话,就学习吧

引用

一. 引用的概念

“引用(Reference)是 C++ 相对于C语言的又一个扩充。引用可以看做是数据的一个别名,通过这个别名和原来的名字都能够找到这份数据。

具体是什么意思呢?

我们这里来举个例子

比如:李逵,在家称为"铁牛",江湖上人称"黑旋风"。

 那你叫铁牛 他会答应你

你叫黑旋风 他也会答应你

代码演示

我们下面来写一段代码试试

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

这段代码是什么意思呢?

我们假设这个a是李逵

那么这个b就是黑旋风的意思了!

我们打印这两个变量的地址来看看

它们的地址一样的 都是李逵!

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

比如说像我们这样子 

这里就是一个错误代码

二. 引用特性

  1. 引用在定义时必须初始化
  2. 一个变量可以有多个引用
  3. 引用一旦引用一个实体,再不能引用其他实体
1. 引用在定义时必须要初始化 

这里如果不初始化就会报错 不用多讲了

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

我们有代码如下

int main()
{
	int a = 10;
	int& b = a;
	int& c = a;

	cout << &a << endl;
	cout << &b << endl;
	cout << &c << endl;
	return 0;
}

这里它们的地址全部都一模一样 就可以说明它们都是一个地址的别名了

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

这句话是什么意思呢?

就拿我们的c来说

他已经引用了a了 还能不能引用其他变量呢

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

	cout << &a << endl;
	cout << &b << endl;
	cout << &c << endl;
	return 0;
}

我们可以发现 它们的地址是没有变化的 那么

c=d;

这一步代码 到底改变了什么呢?

我们画图来看看

我们来验证下我们的理论正确不正确

这里就可以发现 a b c的值全部都变成20了

三. 使用场景

1 . 做参数
2 . 做返回值

1. 做参数

我们来看下面的代码

要求: 交换两个变量的值

void Swap(int x, int y)
{
	int tmp = 0;
	tmp = x;
	x = y;
	y = tmp;

}

可是上面这段代码真的能够交换两个变量的值嘛?

看过我的这两篇博客的同学应该知道 答案是 不能

函数栈帧(上)-CSDN博客

函数栈帧(下)-CSDN博客

为什么呢?

因为x y只是我们要交换的函数的临时拷贝

交换它们的值并不会对要交换的值有什么影响

那么结合我们今天学到的知识

同学们有没有想到一种巧妙的解法呢?

没错! 就是引用传参

我们写出下面这样子的代码

void Swap(int& x, int& y)
{
	int tmp = 0;
	tmp = x;
	x = y;
	y = tmp;

}

将它们的别名传进去 就可以啦

思考题:
我们都知道 在单链表头插尾插的时候 为了防止头指针为空的情况 我们就需要传递一个二级指针进去
这样子很麻烦
那么使用我们的引用机制如何修改它呢?

答案就是! 将指针的别名(引用)作为参数传递进去 那么修改引用参数是不是就可以了?

如果不能理解的话这样子

李逵吃饱了是不是就等于黑旋风吃饱了?

我们之后再来看以下代码

#include <time.h>
struct A { int a[100000]; };

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()
{
	TestRefAndValue();

	return 0;
}

 

我们可以发现使用两个函数运行的时间竟然相差很多!

这是为什么呢?

这里就设计到一个传值和传引用的区别(大家类比下传值和传址)
传值需要将整个a拷贝一份
而我们使用引用的话不会拷贝 时间就差在这里 

2. 做返回值 

还是一样 我们先来看这么两段代码

int Count()
{
	int n = 0;
	n++;
	//...
	return n;
}

int& Count()
{
	static int n = 0;
	n++;
	//...
	return n;
}

这两段代码的返回值都是1

但是它们返回的方式可不同

我们先来看第一个代码

int Count()
{
	int n = 0;
	n++;
	// ...
	return n;
}

当我们调用count()这个函数的时候 它会开辟一个新的内存空间在这里临时空间里面设置一个n变量

将n的值加加

到了这一步的时候

return n;

将n的值放到临时变量(有可能是寄存器)里面返回

再来看看你第二个代码

int& Count()
{
	static int n = 0;
	n++;
	//...
	return n;
}

前面的过程几乎一样

注意! 这时候它的返回参数是 int&

也就是说 它直接是把n的别名(引用)返回过来了!

不知道大家能不能看出来区别

一个是返回到临时变量中 一个值直接返回n的别名(引用)的值

那么这里就引出一个很危险的操作!

int& Count()
{
   int n = 0;
   n++;
   // ...
   return n; }

我们将static去掉 这个时候n彻底变成局部变量了!

这个时候我们打印一下试试看

咦?

竟然还是1 难道说局部全局变量没有影响嘛?

当然不是!!!

我们再来写出以下代码

int& Count()
{
	int n = 0;
	n++;
	//...
	return n;
}
void test()
{
	cout << "hello world" << endl;
}
int main()
{
	int& ret = Count();
	cout << ret << endl;
	test();
	cout << ret << endl;
	return 0;
}

我们发现! 竟然ret变成随机值了!

这是为什么呢?

因为我们实际上得到的ret是n的别名 但是呢在函数结束调用之后所有的参数就被销毁了(这其中也包括n) 当我们运行另外的一个函数来调用栈空间的时候 有可能就将n地址的内容改变了 所以说造成了这个现象

那么这里就引用出两个问题

内存销毁后空间还在吗?

空间还在 但是已经不属于我们了

内存销毁后我们还能访问嘛? 

可以访问 但是里面的数据的读写我们都不能确定

结论 

1 出了函数作用域,返回变量不存在了,不能用引用返回,因为引用返回的结果是未定义的。
2 出了函数作用域,返回变量存在,才能使用引用返回。

优点 

 可以修改返回值

 比如说

 如果说我们使用 int 来接受ret的话那么只能够每次都给ret赋值了

四. 传值传引用的效率比较

参考做参数 使用场景1中的举例

五. 常引用

这里牢记一个概念就好
我们引用一个变量的时候所具有的权限只能小于等于该变量

例如

int main()
{
	//a具有读写功能
	int a = 10;
	int& b = a;
	//可以
	const int& b = a;
	//权限缩小 可以

	const int c = 20;
	int& d = c;
	//不可以 权限放大了
	const int& e = c;
	//可以 权限相等 平移
    return 0;

}
右值为常数问题

常数是不可以被改变的 所以说没有写权限

其实不是不能引用而是权限不匹配

如果一定要可以用以下方式写代码

 const int& a = 10;
	// 读写权限匹配
	cout << a << endl;

 以上就是关于c++引用博主一些浅薄的理解啦
如果出现错误希望大佬们指正!

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

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

相关文章

南京观海微电子----驱动电路中误导通及应对方法

驱动电路中的误导通 功率器件如 MOSFET、IGBT 可以看作是一个受门极电压控制的开关。当门极电压大于开通阈值时&#xff0c;功率器件就会 被开通&#xff0c;而当门极电压低于开通阈值时就会被关断。但是在实际的应用中&#xff0c;由于器件及外围线路寄生参数的影响&#xff0…

C++ —— 哈希详解 - 开散列与闭散列

目录 1. 哈希的概念 1.1 直接定址法 1.2 哈希冲突 1.3 负载因子 1.4 哈希函数 1.4.1 除法散列法/除留余数法 1.4.2 乘法散列法 1.4.3 全域散列法 1.5 处理哈希冲突 1.5.1 开放定址法&#xff08;闭散列&#xff09; 1. 线性探测&#xff08;挨着查找&#xff09; 2.…

NVR批量管理软件EasyNVR大华NVR管理平台如何在Linux环境下部署?

随着视频监控技术的不断进步&#xff0c;NVR&#xff08;网络视频录像机&#xff09;批量管理软件在维护公共安全、提升管理效能方面发挥着越来越重要的作用。EasyNVR作为一款功能强大的NVR批量管理软件&#xff0c;凭借其高效的视频处理能力、灵活的设备接入能力和智能分析功能…

js技能提升——手搓图片组展示——基础积累

// 图片组展示[{name:,src:}]showAltPics(picList[], index0) {if (picList.length 0) {layer.msg("图片路径不对&#xff0c;请重试&#xff01;", { time: 2000 });return false;}//解析展示let inPicListHtml ;let indexPic picList[index];for (let i 0; i &…

<项目代码>YOLOv8 番茄识别<目标检测>

YOLOv8是一种单阶段&#xff08;one-stage&#xff09;检测算法&#xff0c;它将目标检测问题转化为一个回归问题&#xff0c;能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法&#xff08;如Faster R-CNN&#xff09;&#xff0c;YOLOv8具有更高的…

Llama架构及代码详解

Llama的框架图如图&#xff1a; 源码中含有大量分布式训练相关的代码&#xff0c;读起来比较晦涩难懂&#xff0c;所以我们对llama自顶向下进行了解析及复现&#xff0c;我们对其划分成三层&#xff0c;分别是顶层、中层、和底层&#xff0c;如下&#xff1a; Llama的整体组成…

冒泡选择法(c基础)

适合对象c语言初学者。 冒泡选择法 作用对一个数组进行排序。&#xff08;介绍一下数组(c基础)(详细版)-CSDN博客&#xff09; 核心要点 1: 数组元素个数 sz 2: 比较后的交换。 核心思路 进行&#xff08;sz - 1&#xff09;趟&#xff0c;每一趟把最大数的放到末尾。其…

深入浅出《钉钉AI》产品体验报告

1. 引言 随着人工智能技术的迅猛发展&#xff0c;企业协同办公领域迎来了新的变革。钉钉作为阿里巴巴集团旗下的企业级通讯与协同办公平台&#xff0c;推出了钉钉AI助理&#xff0c;旨在提高工作效率&#xff0c;优化用户体验。本报告将对钉钉AI助理进行全面的产品体验分析&am…

【GPTs】Gif-PT:DALL·E制作创意动图与精灵动画

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AIGC | GPTs应用实例 文章目录 &#x1f4af;GPTs指令&#x1f4af;前言&#x1f4af;Gif-PT主要功能适用场景优点缺点 &#x1f4af;小结 &#x1f4af;GPTs指令 中文翻译&#xff1a; 使用Dalle生成用户请求的精灵图动画&#…

一文看懂什么1688跨境(专业解析)

1688跨境是什么? 最近火出圈的一个新词 今天老余带大家走近1688跨境 首先为什么会出现1688跨境&#xff1f; 因为&#xff1a; 新型服务商崛起&#xff0c;反向海淘成趋势 在过去三年&#xff0c;1688涌现了一批新型的服务商: 他们帮助海外B类买家到1688采购&#xff…

SpringBoot+Vue3实现数据可视化大屏

前端工程的地址:UserManagerFront: 数据可视化前端 (gitee.com) 效果展示&#xff0c;可以展现出来了&#xff0c;样式可能还有一些丑。 后端代码 后端主要是拿到数据并对数据进行处理&#xff0c;按照前端需要的格式进行返回即可。 import com.njitzx.entity.Student; impor…

<项目代码>YOLOv8 手机识别<目标检测>

YOLOv8是一种单阶段&#xff08;one-stage&#xff09;检测算法&#xff0c;它将目标检测问题转化为一个回归问题&#xff0c;能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法&#xff08;如Faster R-CNN&#xff09;&#xff0c;YOLOv8具有更高的…

算法定制LiteAIServer摄像机实时接入分析平台烟火识别检测算法

在公共安全领域&#xff0c;火灾的及时发现与处理是保障人民群众生命财产安全的重要手段。传统的烟火检测手段受限于人工巡查的局限&#xff0c;难以做到全天候、无遗漏的监控。然而&#xff0c;随着人工智能技术的飞速发展&#xff0c;LiteAIServer摄像机实时接入分析平台烟火…

JMeter与大模型融合应用之JMeter日志分析服务化实战应用

JMeter与大模型融合应用之JMeter日志分析服务化 引言 在当今的互联网时代,网站和应用程序的性能直接影响到用户的体验和业务的成功。为了保证系统的稳定性和高效性,性能测试成为了软件开发过程中的一个重要环节。在这其中,Apache JMeter作为一款开源的性能测试工具,凭借其…

【Pikachu】任意文件上传实战

将过去和羁绊全部丢弃&#xff0c;不要吝惜那为了梦想流下的泪水。 1.不安全的文件上传漏洞概述 不安全的文件上传漏洞概述 文件上传功能在web应用系统很常见&#xff0c;比如很多网站注册的时候需要上传头像、上传附件等等。当用户点击上传按钮后&#xff0c;后台会对上传的…

STM32 ADC --- 单通道采样

STM32 ADC — 单通道采样 文章目录 STM32 ADC --- 单通道采样cubeMX配置代码修改&#xff1a;应用 使用cubeMX生成HAL工程 需求&#xff1a;有多个通道需要进行ADC采样&#xff0c;实现每次采样只采样一个通道&#xff0c;且可以随时采样不同通道的功能。 cubeMX配置 这里我们…

influxDB 时序数据库安装 flux语法 restful接口 nodjsAPI

安装 Install InfluxDB | InfluxDB OSS v2 Documentation Debian和Ubuntu用户可以用apt-get包管理来安装最新版本的InfluxDB。 对于Ubuntu用户&#xff0c;可以用下面的命令添加InfluxDB的仓库&#xff0c;添加之后即可apt-get 安装influxdb2 wget -q https://repos.influx…

【轻量化】YOLOv10 更换骨干网络之 MobileNetv4 | 模块化加法!非 timm 包!

之前咱们在这个文章中讲了timm包的加法,不少同学反馈要模块化的加法,那么这篇就讲解下模块化的加法,值得注意的是,这样改加载不了mobilebnetv4官方开源的权重了~ 论文地址:https://arxiv.org/pdf/2404.10518 代码地址:https://github.com/tensorflow/models/blob/master…

电气自动控制电路图图例

单相照明双路互备自投供电电路 双路三相电源自投电路 茶炉水加热自动控制电路 简单的温度控制器电路 简易晶闸管温度自动控制电路 用双向晶闸管控制温度电路 XCT-101动圈式温度调节仪控温电路 电接点压力式温度表控温电路 TDA-8601型温度指示调节仪控温电路 XMT-DA数字…

D3 可以加载的数据格式有哪些?(12种)

D3.js 支持多种数据格式&#xff0c;这些格式涵盖了从简单的表格数据到复杂的地理数据。以下是一些常见的数据格式及其加载方法&#xff1a; D3.js 数据加载方法 d3.blob(input, init) 用途: 加载二进制数据&#xff0c;返回一个 Blob 对象。参数: input: 数据源 URL。init: …