C++ -引用-详解

博客主页:【夜泉_ly】
本文专栏:【C++】
欢迎点赞👍收藏⭐关注❤️

在这里插入图片描述

C++ -引用-详解

  • 1.引用基础
    • 1.1是什么
    • 1.2特点
  • 2.引用的意义
  • 3.引用的应用场景
    • 3.1作为参数
    • 3.2作为返回值
      • 传值返回
      • 引用返回
  • 4.权限问题
  • 5.与指针的区别
  • 6.总结

1.引用基础

1.1是什么

引用(Reference)是C++中的一个重要概念,它允许你为变量创建一个别名,从而使你能够简洁地操作同一个变量。

引用通过在类型后加上&符号来定义,例如:

int a = 0;        // 定义一个整型变量a并赋值为0
int& b = a;       // b是a的别名,任何对b的操作都会影响a
int& c = b;       // c也是a的别名,c和b共享同一内存位置

使用cout输出这些变量的地址时,可以清楚地看到它们指向同一块内存空间:

std::cout << &a << std::endl; // 输出变量a的内存地址
std::cout << &b << std::endl; // 输出变量b的内存地址,显示与a相同
std::cout << &c << std::endl; // 输出变量c的内存地址,依旧与a和b相同

运行结果如下图:
在这里插入图片描述
这形象的展示了引用不是定义一个新的变量,而是给已存在的变量取了一个别名
因此,在概念上通常认为,引用不会开辟新的内存空间

1.2特点

如果对其中任何一个变量进行自增操作,所有引用变量都会受到影响:

a++;b++;c++;//一个++,全部++

如当a最初为0时,执行完这三条语句后,a变为3

引用在定义时必须初始化,这点可以和指针做对比:

int& b;//这样写是不行的
int* b;//这样写是可以的

如果不初始化,不能通过编译:
在这里插入图片描述

再看看下面这段代码:

int x = 11;
c = x;

是c变为x的引用(别名),还是x赋值给c?
是赋值,C++的引用不能改变指向

2.引用的意义

虽然直接定义别名在某些情况下看似没有实际意义,但在实际开发中,引用提供了更灵活和安全的参数传递方式,大大提高了代码的可读性和效率。

例如,现在要写一个函数用来交换两个变量的值:

void Swap(int a, int b);

这段代码的问题非常明显——形参是实参的临时拷贝,形参的修改不影响实参。
方法一:使用指针

void Swap(int* a, int* b);

这是在C语言中的常见写法,通过传递指针,能够直接操作变量的内存地址,从而实现交换:

Swap(&a, &b); // 通过地址传递,直接影响原始变量

方法二:使用引用
相比之下,使用引用可以使代码更简洁、直观,避免了指针可能带来的复杂性:

void Swap(int& a, int& b);

传参:

Swap(x, y); // 形参是实参的别名,交换a和b等于交换x和y。

交换指针的方式:
以前:传二级指针。
现在:

void Swap(int*& a, int*& b); // 通过引用传递指针,允许直接修改指针的指向

顺序表尾插的更新:

在处理数据结构时,引用的优势更加明显。例如,以前的写法可能如下:

ListPushBack(struct ListNode** pphead, int x); 
// 传递二级指针

而更新后的写法则显得更加简洁和安全:

ListPushBack(struct ListNode*& phead, int x); 
// 使用引用,减少了指针的复杂性

定义顺序表节点
有书这样写:

typedef struct ListNode
{
	XXXX;
}LTNode,*pLTNode;
//注,*pLTNode指的是:
//typedef struct ListNode* pLTNode;

这时,它们的尾插就能这样定义了:

LTPushBack(pLTNode& phead, int x);

二级指针就这样被干没了🤣。

3.引用的应用场景

3.1作为参数

输出型参数:
形参的改变直接影响实参,避免了额外的内存开销。
提高效率:
在处理大对象或深拷贝类对象时,引用能显著提高效率,降低程序的内存消耗。
例如,对于一个大型结构体,使用引用传递可以避免不必要的拷贝,提升性能:

struct
{
    int a[10000]; // 大对象的定义
};

传参时的速度:引用快于拷贝,引用和指针差不多。

3.2作为返回值

返回值时,如果直接返回一个值,会涉及到创建临时变量的问题。以此为例:

int func(); // 普通返回
int& func(); // 返回引用

传值返回

例如:

int func() {
    int n = 0;
    return n;
}
int main() {
    int ret = func();
}
  • n是直接返回给ret吗?

不是。中间会生成临时变量,有可能是寄存器代替,做为表达式的返回值,再传给ret

  • 为什么?

因为出了作用域,函数栈帧被销毁,n也会被销毁。

如果:

{
static int n = 0;
}
  • 此时,n在静态区了,还会不会生成临时变量?

会,不搞特例。

因此,如果传值返回(全局变量,静态变量,局部变量),都会生成临时变量

如果不想生成,怎么办?

引用返回

int& func();

返回n的别名(引用)
意义:

  1. 大对象、深拷贝类对象。极大提高效率。
  2. 可以获取、修改返回值(如顺序表中:int&SLAt(SQList&s,int pos);

注意事项: 直接返回局部变量的引用是极其危险的,可能导致未定义行为。

  • 栈帧销毁了,但未清理,ret的结果侥幸正确;
  • 栈帧销毁了,且清理了,ret的结果是随机值。
    例如:
int& func()
{
	int n = 0;
	cout << &n <<endl;
	return n;
}

int main()
{
	int& ret = func();//ret是个引用,是n的别名的别名。
	cout << &ret;
	return 0;
}

运行结果如下图:
在这里插入图片描述
会发现,n与ret的地址一样。

再来看看下面这段代码:

int& func(int x)
{
	int n = x;
	n++;
	return n;
}
int main()
{
	int& ret = func(10);
	cout << ret << endl;
	func(20);
	cout << ret << endl;
	return 0;
}

运行结果如下图:
在这里插入图片描述

现在稍作修改,将func(20);改为任一其它函数:

	//func(20);
	printf("HaHa\n");

运行结果如下图:
在这里插入图片描述

最终打印11和随机值。
因为,下一个函数建立的栈帧会覆盖func之前建立的栈帧。
系统不同,也可能都是随机值。
所以,返回局部对象的引用是危险的行为:

  • 危险:传值接收,值取决于清没清理栈帧。
  • 更危险:引用接收。

为了避免这一问题,可以返回一个静态变量的引用:

int& func() 
{
    static int n = 0; 
    // n在静态区,生命周期持续到程序结束
    return n; 
    // 返回静态变量的引用,安全且有效
}

返回静态对象的引用不危险:函数栈帧的销毁不会影响n

4.权限问题

引用过程中,权限不能放大

const int a = 0;
int& b = a;// 不行
int c = a;// 可以

c只拷贝了a的值,c的修改不会影响a,权限没有放大。
b的修改会影响a,权限放大,编译不能通过。
在这里插入图片描述

int x = 0;
int& y = x;// 可以
const& z = x;// 可以,缩小了z作为别名的权限

权限可以平移、缩小

再看下面这段代码:

double d = 1.1;
int i = d;
int& ri = d;

在这里插入图片描述
需注意,这并不是因为类型不同,而是权限的问题:
在这里插入图片描述
为什么?
因为发生类型转换时,如整型提升、强制转换、隐式类型转换,都会产生一个新的临时变量:
在这里插入图片描述
而这个临时变量具有常性,所以int&不行的原因是权限的放大。

再例如:

int func1()
{
	static int x = 0;
	return x;
}
int& func2()
{
	static int x = 0;
	return x;
}
int main()
{
	int ret1 = func1();
	int& ret2 = func1();
	const int& ret3 = func1();

	int& ret4 = func2();
	const int& ret5 = func2();
	return 0;
}

其中,只有ret2会报错:
在这里插入图片描述
ret1:拷贝,无引用,不涉及权限。
ret2:权限放大,传值返回的不是x,而是临时变量,临时变量具有常性。
ret3:权限平移。
ret4:权限平移。
ret5:权限缩小。

5.与指针的区别

int a = 0;

语法层面:不开空间,是对a取别名

int& ra = a;
ra = 10;

语法层面:开空间,存的是a的地址

int* pa = &a;
*pa = 20;

而从底层汇编指令实现的角度看,引用是类似指针的方式实现的:
在这里插入图片描述
指针会出现野指针、空指针等情况,相比之下,引用更加安全。

6.总结

  • 引用传参: 引用是一种高效且安全的参数传递方式,适用于几乎所有场景,特别是在处理大型对象和复杂数据结构时,能够显著提高性能。

  • 引用返回: 返回引用时应谨慎,仅在全局、静态和堆对象中使用,以避免潜在的未定义行为。直接返回局部变量的引用是极其危险的,因为一旦局部变量超出作用域,引用将指向无效的内存地址,从而可能导致程序崩溃或不可预知的结果。

  • 引用与指针的区别: 虽然在语法上引用和指针都可以用来间接访问变量,但引用更安全,不会出现野指针或空指针的情况。它们的实现原理相似,但在使用时,引用提供了更简洁和直观的语法。

  • 总结: 通过引用,我们可以在保持高效的同时,使代码更加易读和安全。无论是在参数传递还是返回值处理中,引用都是一种极具价值的工具。

在这里插入图片描述


希望本篇文章对你有所帮助!并激发你进一步探索编程的兴趣!
本人仅是个C语言初学者,如果你有任何疑问或建议,欢迎随时留言讨论!让我们一起学习,共同进步!

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

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

相关文章

计算机网络期末复习真题(附真题答案)

前言&#xff1a; 本文是笔者在大三学习计网时整理的笔记&#xff0c;哈理工的期末试题范围基本就在此范畴内&#xff0c;就算真题有所更改&#xff0c;也仅为很基础的更改数值&#xff0c;大多跑不出这些题&#xff0c;本文包含简答和计算等大题&#xff0c;简答的内容也可能…

话术挂断之后是否处理事件

文章目录 前言联系我们解决方案方案一方案二 前言 流程&#xff1a;自动外呼进入机器人话术。问题&#xff1a;在机器人放音时用户挂断后&#xff0c;话术还会继续匹配流程&#xff0c;如果匹配上的是放音节点&#xff0c;还会进行放音&#xff0c;那么在数据库表conversation…

stm32四足机器人(标准库)

项目技术要求 PWM波形的学习 参考文章stm32 TIM输出比较(PWM驱动LED呼吸灯&&PWM驱动舵机&&PWM驱动直流电机)_ttl pwm 驱动激光头区别-CSDN博客 舵机的学习 参考文章 stm32 TIM输出比较(PWM驱动LED呼吸灯&&PWM驱动舵机&&PWM驱动直流电机)…

Stream流的初步认识,Stream流的思想和获取Stream流

一.Stream流的作用 package com.njau.my_stream;import java.util.ArrayList;/*** 目标&#xff1a;认识Stream流* 案例&#xff1a;将以“张”开头的人名筛选出来到一个新的集合中去&#xff0c;再将其中三个字的名字的筛选出来到新集合中去*/ public class StreamDemo1 {pub…

畅阅读小程序|畅阅读系统|基于java的畅阅读系统小程序设计与实现(源码+数据库+文档)

畅阅读系统小程序 目录 基于java的畅阅读系统小程序设计与实现 一、前言 二、系统功能设计 三、系统实现 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大厂码农|毕设布道师…

【FPGA开发】Xilinx FPGA差分输入时钟的使用方法

正文 以前在使用ZYNQ的领航者ZYNQ7020进行FPGA学习时&#xff0c;它们使用的单端50M的输入时钟&#xff0c;在verlog代码编写上比较简单&#xff0c;而现在使用Alinx的AXU3EG开发板时&#xff0c;发现它使用的是200M的差分输入时钟&#xff0c;哪这个时候&#xff0c;输入时钟要…

【IEEE PDF eXpress】格式不对

目录 一、问题二、解决方法 一、问题 word的文档&#xff0c;用IEEE PDF eXpress网站生成pdf后&#xff0c;提交论文出现错误&#xff1a; Document validation failed due to the following errors: Content exceeds IEEE template margins for its format (Page 1:Bottom).…

Microsoft Edge 五个好用的插件

&#x1f423;个人主页 可惜已不在 &#x1f424;这篇在这个专栏 插件_可惜已不在的博客-CSDN博客 &#x1f425;有用的话就留下一个三连吧&#x1f63c; 目录 Microsoft Edge 一.安装游览器 ​编辑 二.找到插件商店 1.打开游览器后&#xff0c;点击右上角的设置&#…

课设实验-数据结构-单链表-文教文化用品品牌

题目&#xff1a; 代码&#xff1a; 正解&#xff1a; #include<stdio.h> #include<stdlib.h> #include<string.h> #define MaxSize 10 //定义顺序表最大长度static int result; //字符串比较结果 static int i; //循环初始值 static bool flag; //记录结…

单目3d重建DUSt3R 笔记

目录 DUSt3R 三维重建 报错RecursionError: maximum recursion depth exceeded in comparison 报错 numpy.core.multiarray failed to import 报错Numpy is not available 解决 升级版mast3r 速度变慢 修改了参数设置脚本&#xff1a; 测试效果 操作技巧 DUSt3R 三维重…

Docker仓库搭建

目录 一、Docker Hub 二、私有Registry仓库搭建 1、下载并开启仓库镜像registry 2、Registry加密传输 3、建立一个registry仓库 4、为客户端建立证书 5、测试 6、为仓库建立登录认证 三、Harbor仓库搭建 Docker 仓库&#xff08;Docker Registry&#xff09; 是用于存…

V3D——从单一图像生成 3D 物体

导言 论文地址&#xff1a;https://arxiv.org/abs/2403.06738 源码地址&#xff1a;https://github.com/heheyas/V3D.git 人工智能的最新进展使得自动生成 3D 内容的技术成为可能。虽然这一领域取得了重大进展&#xff0c;但目前的方法仍面临一些挑战。有些方法速度较慢&…

Part_one C/C++语言数据类型、运算符与表达式

1.0 编写第一个C程序 1.打开Visual Studio点击"创建新项目" 2.点击"空项目"&#xff0c;并点击"下一步" 3.设置"项目名称"并"设置地址" 4.打开项目后&#xff0c;右击"源文件"并选择"添加"的"新建…

基于yolov8深度学习的120种犬类检测与识别系统python源码+onnx模型+评估指标曲线+精美GUI界面目标检测狗类检测犬类识别系统

【算法介绍】 基于YOLOv8深度学习的120种犬类检测与识别系统是一款功能强大的工具&#xff0c;该系统利用YOLOv8深度学习框架&#xff0c;通过21583张图片的训练&#xff0c;实现了对120种犬类的精准检测与识别。 该系统基于Python与PyQt5开发&#xff0c;具有简洁的UI界面&a…

程计软考题2-编译、解释程序翻译阶段

(一) 编译器和解释器的工作阶段 1.编译和解释与源程序的区别 分析&#xff1a;编译和解释是语言处理的两种基本方式。 编译过程包括词法分析、语法分析、语义分析、中间代码生成、代码优化和目标代码生成等阶段&#xff0c;以及符号表管理和出错处理模块。 解释过程在词法、语…

数字经济与新质生产力:地理信息与遥感视角下的深度分析

在数字化浪潮的推动下&#xff0c;我们正见证着生产力的一次历史性飞跃。数字经济如何重塑生产力的三大要素&#xff1a;劳动对象、劳动资料和劳动者&#xff1f;让我们来深度分析数字经济如何推动新质生产力的发展。 一、数字经济与地理信息的融合 地理信息与遥感技术是数字…

【数据结构】什么是红黑树(Red Black Tree)?

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:数据结构 ⚙️操作环境:Visual Studio 2022 目录 &#x1f4cc;红黑树的概念 &#x1f4cc;红黑树的操作 &#x1f38f;红黑树的插入操作 &#x1f38f;红黑树的删除操作 结语 &#x1f4cc;红黑树的概念 我们之前学过了…

PyGWalker:让你的Pandas数据可视化更简单,快速创建数据可视化网站

1、PyGWalker应用: 在数据分析的过程中,数据的探索和可视化是至关重要的环节,如何高效地将分析结果展示给团队、客户,甚至是公众,是很多数据分析师和开发者面临的挑战,接下来介绍的两大工具组合——PyGWalker与Streamlit,可以帮助用户轻松解决这个问题,即使没有复杂的代…

cheese安卓版纯本地离线文字识别插件

目的 cheese自动化平台是一款可以模拟鼠标和键盘操作的自动化工具。它可以帮助用户自动完成一些重复的、繁琐的任务&#xff0c;节省大量人工操作的时间。可以采用Vscode、IDEA编写&#xff0c;支持Java、Python、nodejs、GO、Rust、Lua。cheese也包含图色功能&#xff0c;识别…

HarmonyOS鸿蒙 Next 实现协调布局效果

HarmonyOS鸿蒙 Next 实现协调布局效果 ​ 假期愉快! 最近大A 的涨势实在是红的让人晕头转向&#xff0c;不知道各位收益如何&#xff0c;这会是在路上&#xff0c;还是已经到目的地了? 言归正传&#xff0c;最近有些忙&#xff0c;关于鸿蒙的实践系列有些脱节了&#xff0c;…