C++——引用
引用的概念
引用不是新定义一个变量,而是给已存在的变量取一个别名,编译器不会因为引用变量而开辟内存空间,它和它引用的变量公用同一块空间。
相当于是给被引用的变量取了一个小名,但是相当于是同一个变量。
类型& 引用变量名(对象名) = 引用实体
void TestRef()
{
int a = 10;
int& ra = a;// 定义引用类型
printf("%p\n",&a);
printf("%p\n",&ra);
}

注意:引用类型必须和引用实体是同种类型的数据。
对引用变量的修改就是对引用实体的修改
这里可以对输出型参数进行应用
1.交换函数
void Swap(int& x, int& y)
{
int tmp = x;
x = y;
y = tmp;
}
int main()
{
int a = 10, b = 20;
Swap(a,b);
cout << "a = " << a << endl << "b = " << b;
}
![]()
2.单链表
本来,对于单链表的pushback而言,我们要传入二级指针
typedef struct SListNode
{
SLDataType data;
struct SListNode* next;
}SLnode;
void SLPushBack(SLNode** pphead, SLDateType x)
{
SLNode** pphead, SLDateType x;
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
SLNode* tail = *pphead;
while(tail -> next != NULL)
{
tail = tail -> next;
}
tail -> next = newnode;
}
}
有了引用,我们就可以这样写
typedef struct SListNode
{
SLDataType data;
struct SListNode* next;
}SLnode;
void SLPushBack(SLNode*& phead, SLDateType x)
{
if (phead == NULL)
{
phead = newnode;
}
else
{
SLNode* tail = phead;
while(tail -> next != NULL)
{
tail = tail -> next;
}
tail -> next = newnode;
}
}
引用特性
1.引用在定义时必须初始化
void TestRef()
{
int a = 10;
// int&ra ;//该条语句编译时会出错
int& ra = a;
int& rra = a;
}
2.一个变量可以有多个引用

还可以给别名再取别名,这样,所有的名字其实都指的是同一个变量。
3.引用一旦引用一个实体,再不能引用其他的实体
常引用
void TestConstRef()
{
const int a = 10;
//int& ra = a; //该语句编译时会出错,因为a为常量
const int& ra = a;
//int& b = 10; //该句编译时会出错,因为b为常量
const int& b = 10;
double b = 12.34;
//int& rd = d; //该语句编译时会出错,因为类型不同
const int& rd = d;
}
做返回值
正常情况下的传值返回:
int Count()
{
int n = 0;
n++;
return n;
}
int main()
{
int ret = Count();
cout << ret << endl;
}
如果将代码改成
int& Count()
{
int n = 0;
n++;
return n;
}
int main()
{
int ret = Count();
cout << ret << endl;
cout << ret << endl;
}

此时,相当于是Count函数的返回值是n的一个引用
虽然短期内可以返回正确的值
但是,我们知道,出了Count函数后,n变量就会被销毁,此时我们利用它的引用取访问它,其实就相当于之前的野指针问题,会有很大的安全问题。
如果将代码再改成下面的情况:
int& Count()
{
int n = 0;
n++;
return n;
}
int main()
{
int& ret = Count();
cout << ret << endl;
cout << ret << endl;
}
相当于ret和返回值都是n这个变量

第二次访问的时候出现了随机值。
因为cout输出本身也是调用了函数,在Count函数调用完后,它的栈帧销毁了(其中也包括n的那块空间),紧接着cout开始调用函数,这时,cout调用的函数的栈帧就可能会将之间的Count所在的栈帧进行覆盖,同理,n所在的那块栈帧就可能会被各种值覆盖,因此,第二次通过n的引用访问n时,就会有可能得到一个随机值。
错误示范
int& Add(int a, int b)
{
int c = a + b;
return c;
}
int main()
{
int& ret = Add(1,2);
Add(3,4);
cout << "Add(1,2) = " << ret << endl;
return 0;
}
![]()
同样的道理,在不清栈帧的情况下,Add(1,2)函数执行的时候开辟的栈帧的c所在的空间,会被后面再次调用Add(3,4)时覆盖,得到7。
结论:
如果函数返回时,出了函数作用域,如果返回对象还在(还没还给系统),则可以使用引用返回,如果已经还给系统了,则必须使用传值返回。
什么情况下可以使用传引用返回
1.返回的变量是全局对象(这样出了函数,变量并不会被销毁)
2.返回的对象是静态对象
传引用传参的作用(任何时候都可以)
1.提高效率
2.输出型参数(形参的修改,影响的实参)
传引用返回的作用(出了函数作用域对象还在才能用)
1. 提高效率
2.可以修改返回对象:
小例:单链表的访问和修改:
单链表:
struct SepList
{
int *a;
int size;
int capacity;
};
c语言:
//访问第i个位置的值
int SLAT(struct SepList* ps, int i)
{
assert(i < ps->size);
return ps->a[i];
}
//将第i个位置的值修改为x
void SLModify(struct SepList* ps, int i, int x)
{
assert(i < ps-> size);
ps->a[i] = x;
}
c++引用:
//访问或修改第i个位置的值
int& STAT(struct SepList* ps, int i)
{
assert(i < ps -> size);
return ps->a[i];
}
此时,引用返回如果想要修改第i个位置的值,则直接修改即可,因为函数的返回是ps->a[i]的一个引用,所以此函数既可以访问,也可以修改。
STAT(ps,i) = 1;
在引用的过程中,权限可以平移,可以缩小,但不可以放大
小例:
int main()
{
const int a = 0;
//权限的放大:本来a变量不可以修改的,但是引用变量b是可以被修改的,所以这里不能这么写
int& b = a;
//权限的平移:这里b和a都不能被修改,所以权限是相同的,可以这样写
const int& b = a;
//权限的缩小:本来a是可以被修改的,但是引用变量b不可以,这样就会把a的权限缩小,但是这样是合法的
int a = 0;
const int& b = a;
return 0;
}
还有一种情况:
int a = 7;
double& b = a;
这样写也是会报错的:
因为int类型的变量赋值给double时,不是直接给的,中间会有一个double类型的临时变量,也就是隐式转换,先将int类型的a转换成double类型,赋值给临时变量,然后将这个临时变量的值给b。
而我们知道,临时变量具有常量性,所以如果这样写,会有权限放大的现象。
如果改为:
int a = 7;
const double& b = a;
就不会报错了。
类似的:
int fun()
{
int a = 0;
return a;
}
int main()
{
int& ret = fun();
return 0;
}
这里也会报错:
因为,fun函数不是直接返回a,而是返回a的一份临时拷贝,也是具有常量性,所以这里的int& ret = fun()也存在权限放大的问题。
但是改为const int& ret = fun()就可以了。
这里的临时变量也会因为这里的引用被自动延长生命周期,直到不再使用为止。
引用和指针的联系

从汇编角度来看,引用其实和指针在底层其实是一种操作,也就是说底层其实没有引用,只有指针。

执行++操作的步骤也是一样的。
引用和指针的区别
1.引用概念上定义一个变量的别名,指针存储了一个变量指针。
2.引用在定义时必须初始化,指针没有要求。
3.引用在初始化时引用一个实体后,就不能再引用其他实体了,而指针可以在任何一个同类型实体
4.没有NULL引用,但又NULL指针。
5.在sizeof中含义不同,引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
6.引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小。
7.有多级指针,但是没有多级引用。
8.访问实体方式不同,指针需要显示解引用,引用编译器自己处理。
9.引用比指针使用起来相对更安全。
相关文章:
C++——引用
引用的概念 引用不是新定义一个变量,而是给已存在的变量取一个别名,编译器不会因为引用变量而开辟内存空间,它和它引用的变量公用同一块空间。 相当于是给被引用的变量取了一个小名,但是相当于是同一个变量。 类型& 引用变…...
Flask入门一 ——虚拟环境及Flask安装
Flask入门一 ——虚拟环境及Flask安装 在大多数标准中,Flask都算是小型框架,小到可以称为“微框架”,但是并不意味着他比其他框架功能少。Flask自开发伊始就被设计为可扩展的框架。Flask具有一个包含基本服务的强健核心,其他功能…...
接口测试json入参,不同类型参数格式书写
接口json入参,不同类型参数格式 1、String 入参:A(String),B(String) 格式:{"A":"值a","B":"值b"} 示例: 接口测试入参这么…...
go web框架 gin-gonic源码解读03————middleware
go web框架 gin-gonic源码解读03————middleware(context) 今天打完游戏有空整理整理之前看的gin的中间件设计,go的中间件设计相较于前两站还是蛮简单,蛮容易看懂的,所以顺便把context也一起写一下。 中间件是现在w…...
win10电脑记事本在哪里?电脑记事本如何查看字数?
在日常工作中,我们会遇到许多需要记录的信息和事项,而使用电脑记事本工具可以帮助我们方便地保存、管理这些内容。无论是记录工作会议的要点、制定工作计划,还是记录灵感和创意,电脑记事本都是非常实用的工具。 那么win10电脑记事…...
【微服务】06-安全问题
文章目录 1.反跨站请求伪造1.1 攻击过程1.2 攻击核心1.3 如何防御1.4 使用AntiforgeryToken机制来防御用到的类 2. 防开发重定向共计2.1 攻击过程2.2 攻击核心2.3 防范措施 3.防跨站脚本3.1 攻击过程3.2 防范措施 4.跨域请求4.1 同源与跨域4.2 CORS过程4.2 CORS是什么4.3 CORS请…...
js的this指向问题
代码一: 这段代码定义了run函数、obj对象,然后我们把run函数作为obj的方法。 function run(){console.log(this);}let obj{a:1,b:2};obj.runrun;obj.run(); 那么我们调用obj的run方法,那么这个方法打印的this指向obj。 分析:即…...
Redis常用数据类型及命令
Redis 常用数据类型 常用数据类型 主要是指value类型 key都是字符串类型的 各种数据类型对应的特点 应用场景 哈希:一般来存储一些对象 列表:存一些跟顺序有关系的数据,比如朋友圈点赞 集合:一般用来做运算,交集&a…...
软件工程(六) 面向对象分析(OOA)之UML图特点
1、UML 面向对象分析里面有一个非常重要的工具叫UML,UML不仅在工作中非常重要,在考试当中也是非常重要的,即作为上午综合体,又大概率又会出现在下午的案例分析中,作为一个25分的大题。 UML叫做统一建模语言,它主要用于需求分析和软件的设计,来做一些模型的制作。比如我…...
QT 消息对话框按钮显示
前言 搞QT嘛,大多数都是军工。都要国产化,而且消息对话框的按钮的英文也不是很得劲,所以需要汉化。使用静态函数的按钮就是显示英文,汉化的代码如下。 void Widget::on_pushButton_clicked() {QMessageBox box(QMessageBox::Inf…...
平衡二叉树及其应用详解
平衡二叉树 定义与性质 平衡二叉树(Balanced Binary Tree)是计算机科学中的一种数据结构,它是二叉排序树的一种特殊情况。 平衡二叉树满足以下性质: 左子树和右子树的高度差不超过 1。也就是说,对于任意节点&#…...
vue3+ ts ts语法在script写不知道为啥一直报错
在vue3页面中写ts语法 发现识别不了 一直报错 1.出现这种问题的话,首先查看自己写的有没有问题,没有问题的话 2.再查看 script里边有没有写 lang"ts" <script setup lang"ts">解析 setup:是vue3在单文件组件 (SFC) 中使用 composition …...
c#写的端口监听,程序退出后,再次运行提示端口占用,且进程不存在
我用c#写了一个监听29999端口,进程结束后再次启动发现端口被占用,但是运行netstat -ano | findstr 29999找到进程ID后,却没有这个进程 经查询这个监听29999进程虽然没了,但是要找到他的父进程,把父进程关闭了才可以,参…...
跨域案例go gf ,请求代理,前端请求后端A转发给多个后端B
跨域案例go gf ,请求代理,前端请求后端A转后端B 案例:从前端请求后端A(路径携带argusx),后端A转发请求到多个不同地区(可一个)后端B(切掉argusx,其他不变进行请求)&…...
9.4 集成功率放大电路
OTL、OCL 和 BTL 电路均有各种不同输出功率和不同电压增益的集成电路。应当注意,在使用 OTL 电路时,需外接输出电容。为了改善频率特性,减小非线性失真,很多电路内部还引入深度负反馈。这里以低频功放为例。 一、集成功率放大电路…...
Java“牵手“拼多多商品详情数据、拼多多优惠券信息、拼多多到手价信息获取方法,拼多多API实现批量商品数据抓取示例
拼多多商城是一个网上购物平台,售卖各类商品,包括服装、鞋类、家居用品、美妆产品、电子产品等。要获取拼多多商品详情数据,您可以通过开放平台的接口或者直接访问拼多多商城的网页来获取商品详情信息。以下是两种常用方法的介绍:…...
亚马逊云科技 re:Inforce 大会云安全合规与技术实践及 Security Jam 大赛,快来报名吧!...
2023年8月31日在北京 亚马逊云科技 re:Inforce 大会 首次登陆中国! 我们期待您的莅临, 并与您一起迎接 AI 时代, 开启全面智能的安全旅程! 在13:00-17:00的 培训与动手实验环节中 云安全合规与技术实践 及 Security Jam 大赛…...
网络安全(黑客技术)学习手册
1.网络安全是什么 网络安全可以基于攻击和防御视角来分类,我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术,而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 2.网络安全市场 一、是市场需求量高; 二、则是发展相对成熟…...
泡泡玛特回应头部IP营收增速放缓:IP上市时间不固定
8月23日,针对今年上半年头部IP营收增速放缓问题,泡泡玛特(09992.HK)管理层在业绩会上解释称,每个IP上市时间并不固定,单从上半年看同比增长会有偏差,而随着下半年两个新系列的推出,全…...
很干的 Nginx
🎨 前言 本篇文章有些概念性的东西,是结合自己的理解表达出来的,可能有些理解不到位的地方。希望多多指教,谢谢大家。 红包献上 🧧🧧🧧🧧🧧🧧🧧…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例
一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...
C++.OpenGL (14/64)多光源(Multiple Lights)
多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...
若依登录用户名和密码加密
/*** 获取公钥:前端用来密码加密* return*/GetMapping("/getPublicKey")public RSAUtil.RSAKeyPair getPublicKey() {return RSAUtil.rsaKeyPair();}新建RSAUti.Java package com.ruoyi.common.utils;import org.apache.commons.codec.binary.Base64; im…...
raid存储技术
1. 存储技术概念 数据存储架构是对数据存储方式、存储设备及相关组件的组织和规划,涵盖存储系统的布局、数据存储策略等,它明确数据如何存储、管理与访问,为数据的安全、高效使用提供支撑。 由计算机中一组存储设备、控制部件和管理信息调度的…...
