C++ 引用 - 引用的特点|在优化程序上的作用

news2024/11/6 8:07:21

引用是C++ 的一个别名机制,所谓别名,就是同一块内存共用多个名字,每个名字都指的是这片空间,通过这些别名都能访问到同样的一块空间。

就像鲁迅和周树人是同一个人。

                                                                                                                            ——鲁迅

一、引用的基本用法

int a = 10;
int& ref = a; // ref 是 a 的引用

ref = 20; // 这也会改变 a 的值
std::cout << a; // 输出 20

需要注意的是,这里定义的引用变量ref并不会在内存上开辟新的空间,而是给a对应的那片空间取了一个新的名字,现在通过ref也能访问到那片空间了。

`&`符号我们不会陌生,在之前他的名字是取地址符,在C++中它还有新的作用:定义引用

在使用中,主要有这两个区别和联系:

   1.定义引用时:`&`在类型之后,表示该变量是引用。

int& ref = a;

  2. 获取地址时:`&`在变量前边, 表示获取该变量的地址。

int* ptr = &a;

二、引用的特点

  • 必须初始化:引用在声明时必须进行初始化。
    int& a; //错误的!!
  • 不可重新绑定:初始化后不能再指向其他变量。
    int a = 0;
    int b = 0;
    
    int& ref = a;
    int& ref = b; //错误的!!
  • 类型一致:引用的类型必须与其引用的变量类型一致。
    int a = 0;
    float& b = a; //错误的!!
  • 权限不能放大:在引用的过程中,权限可以平移,缩小,但不能放大
    const int a = 0;
    int& b = a; //权限的放大
    const int& b = a; //权限的平移
     
    int c = 0;
    const int& d = c; //权限的缩小
  • 多个别名:一个变量可以有多个引用。
    int a = 0;
    int& ref1 = a;
    int& ref2 = a;
  • 没有独立空间:在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间(在底层实现上实际是有空间的,因为引用是按照指针方式来实现的)
    int a = 0;
    int& ref = a;
  • 没有“二级引用”、没有“空引用”
    int a = 0;
    int& ref1 = NULL; //错误的!!
    int&& ref2 = a; //错误的!!

三、引用的作用

以前的交换函数我们只能通过传递参数的指针来实现,而有了引用,我们可以通过给要交换的参数取别名的方式来实现:

#include<iostream>
using namespace std;

void swap(int& a, int& b)
{
	int t = a;
	a = b;
	b = t;
}

int main()
{
	int i = 6, j = 2;
	swap(i, j);

	cout << i << endl;
	cout << j << endl;
	return 0;
}

i,j是实参,a,b是形参,且由于我们在形参类型后加了`&`,也就是说定义了a,b分别是i,j的别名,现在a,b分别代表i,j的那片空间了,所以在交换函数中我们可以通过直接交换a,b的值来交换i,j的值, 从而实现了实参传给形参,形参的改变可以改变实参的功能。

再来看一个顺序表的例子,这是没有引用前我们的写法:

struct SeqList
{
	int a[100];
	int size;
	int capacity;
};

//C实现的写法
int SLFind(struct SeqList* p, int i)
{
	// ... ...
	return p->a[i];
}

void SLModify(struct SeqList* p, int i, int x)
{
	p->a[i] = x;
}

int main()
{
	struct SeqList s;
	// ... ...
	SLFind(&s, 0);

	SLModify(&s, 0, 1);

}

要实现查找和修改这两个功能,我们分别需要`SLFind`和`SLModify` 两个函数来实现,而且需要传递顺序表结构的指针才能进行对值的修改。、

而有了引用,首先我们可以优化参数的传递:改为定义引用来接收实参,从而达到修改形参实现修改实参的功能。其次我们还可以优化程序,用一个函数就能实现修改与查找的功能:

struct SeqList
{
	int a[100];
	int size;
	int capacity;
};

//CPP实现的接口
int& SLFind(struct SeqList& p, int i)
{
	// ... ...
	return p.a[i]; // 返回引用,允许对顺序表元素进行修改
}

int main()
{
	struct SeqList s;
	// ... ...
	SLFind(s, 0) = 1;  //Modify
	cout << SLFind(s, 0) << endl;  //Find
}

在上面这段代码中,我们发现引用也可以作为返回值,但其实这一作用在某些情况下是不安全的,可以看下面这个例子:

int& add(int a, int b)
{
	int n = a + b;
	return n; //返回n的别名
}

int main()
{
	int rst = add(1, 2);
	cout << rst << endl;

	return 0;
}

这段代码的输出值是多少? 是3吗?

这段代码其实存在一个严重安全的问题:add函数返回的是局部变量 n 的引用。在 add 函数执行完毕后,局部变量 n 的生命周期结束,其内存被释放,返回的引用将指向一个已经释放的内存位置。这就像指针里我们提到的野指针。所以,具体输出的值取决于编译器,如果内存释放后编译器将这部分内存置为随机值,那么输出的值就是一个随机值。

如果改成这样呢?

int& add(int a, int b)
{
	int n = a + b;
	return n; //返回n的别名
}

int main()
{
	int& rst = add(1, 2);
	cout << rst << endl;
    cout << rst << endl;

	return 0;
}

在上一段代码是将 n 的值拷贝给 rst, 输出的 rst 取决于 存放 n 的那片内存有没有被置为随机值。

而现在变成了 rst 是 n 别名,两次打印 rst 的值可能是不同的,原因:

接下来调用 cout 函数,新建立的栈帧在原来的 add 的那片空间中

由于调用前 n 的值首先被压入栈帧,所以第一次能够成功读取到 n 的值,但第二次输出的值就是不确定的了,因为 cout 可能会覆盖到存放 n  的值的那片空间(这取决于cout所需要的栈帧空间),如果覆盖到了,那么第二次调用 cout 打印 n 的值就是随机数了。

综上所述,引用的作用有:

  1. 函数参数传递:避免拷贝,提高效率。尤其是对于传递较大的结构体时,效率的提升是显著的;
  2. 实现实参传给形参,形参的改变可以改变实参;
  3. 引用作为返回值:通过返回引用,可以在函数外部修改函数内部的变量。同时避免返回值的拷贝,从而提高性能,尤其是在返回大型对象时效果显著。

    注意,引用必须绑定有效对象:函数返回引用时,必须确保返回的引用绑定到一个有效的对象。避免返回局部变量的引用,因为局部变量在函数结束时会被销毁。

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

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

相关文章

磁盘管理 磁盘介绍 MBR

track&#xff1a;磁道&#xff0c;就是磁盘上同心圆&#xff0c;从外向里&#xff0c;依次1号、2号磁道..... sector&#xff1a;扇区&#xff0c;将磁盘分成一个一个扇形区域&#xff0c;每个扇区大小是512字节&#xff0c;从外向里&#xff0c;依次是1号扇区、2号扇区... cy…

LLVM Cpu0 新后端 系列课程总结

想好好熟悉一下llvm开发一个新后端都要干什么&#xff0c;于是参考了老师的系列文章&#xff1a; LLVM 后端实践笔记 代码在这里&#xff08;还没来得及准备&#xff0c;先用网盘暂存一下&#xff09;&#xff1a; 链接: https://pan.baidu.com/s/1yLAtXs9XwtyEzYSlDCSlqw?…

基于springboot实现问卷调查系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现问卷调查系统演示 摘要 传统信息的管理大部分依赖于管理人员的手工登记与管理&#xff0c;然而&#xff0c;随着近些年信息技术的迅猛发展&#xff0c;让许多比较老套的信息管理模式进行了更新迭代&#xff0c;问卷信息因为其管理内容繁杂&#xff0c;管理数…

【全开源】Java AI绘画MJ绘画源码小程序APP公众号源码AI绘图

&#x1f3a8; 探索AI绘画的奥秘 一、引言&#xff1a;AI绘画的魅力 &#x1f308; 在这个数字化飞速发展的时代&#xff0c;AI绘画已经不再是遥不可及的梦想。通过源码小程序&#xff0c;我们可以轻松探索AI绘画的奥秘&#xff0c;感受科技与艺术的完美结合。今天&#xff0…

ubuntu18.04 配置 mid360并测试fast_lio

1.在买到Mid360之后&#xff0c;我们可以看到mid360延伸出来了三组线。 第一组线是电源线&#xff0c;包含了红色线正极&#xff0c;和黑色线负极。一般可以用来接9-27v的电源&#xff0c;推荐接12v的电源转换器&#xff0c;或者接14.4v的电源转换器。 第二组线是信号线&#x…

组件之间的通信方式

【 0 】前言 Vue 是一个渐进式的 JavaScript 框架&#xff0c;通过组件化的方式构建应用。在 Vue 中&#xff0c;组件通信指的是不同组件之间传递数据、共享数据或调用方法等行为。在前端开发中&#xff0c;子组件和父组件之间的通信是非常常见和重要的。 【 1 】父子组件通信…

SMT智能车间MES系统的实施方案

SMT行业中MES系统实施的关键特征&#xff1a; SMT包括上下板设备&#xff0c;印刷设备&#xff0c;贴片设备&#xff0c;回流焊炉&#xff0c;AOI。AOI的全称是自动光学检查&#xff0c;它基于光学原理来检测焊接生产中遇到的常见缺陷。测试设备&#xff0c;测试设备等MES解决…

如何用多媒体沙盘实现智能交互体验?

随着多媒体技术在内容展示领域的迅猛进步&#xff0c;智能化信息交互方式已然跃升为公众瞩目的焦点&#xff0c;而展厅作为信息传递与产品展示的核心阵地&#xff0c;正面临着提升交互体验、强化信息传递效果的迫切需求。因此&#xff0c;以多媒体沙盘、LED屏幕等创新装置为媒介…

AIGC实践|用AI制作视频短片创作全流程

前言&#xff1a; 在深入探讨了AI在动态有声绘本和小游戏开发的应用之后&#xff0c;本次我们将聚焦于视频创作领域。本篇文章将详细展示如何使用AI工具从概念构思到画面生成再到最终成片的全过程&#xff0c;涵盖剧本创作、分镜头设计、视觉效果生成及音乐配制等各个阶段。让…

无人机的发展

朋友们&#xff0c;你们知道吗&#xff1f;无人机的发展之路可谓是科技界的一股清流&#xff0c;风头正劲啊&#xff01;从最初简单的遥控飞机到现在各种智能功能的加持&#xff0c;无人机真是越来越神奇了&#xff01; 首先&#xff0c;无人机在航拍领域大放异彩&#xff01;无…

Redis 持久化的奥秘:主线程、子进程与后台线程的区别及潜在阻塞风险

1. 主线程、子进程和后台线程的联系与区别 Redis 是一个高性能的键值数据库,以其快速的响应速度和丰富的功能集,广泛应用于各种应用场景。理解 Redis 的线程和进程模型有助于更好地优化其性能。下面,我们将详细探讨 Redis 中的主线程、子进程和后台线程的联系与区别。 进程…

一文详解:信息化/数字化以及数智化的区别与联系

数字化转型是现代企业在竞争激烈的市场环境中保持竞争力的关键策略。数字化转型通常被分为三个阶段&#xff1a;信息化、数字化和数智化。 每个阶段都有其独特的特点和挑战&#xff0c;下面将详细阐述这三个阶段&#xff0c;并通过实际案例来说明其应用的区别和效果。 低成本起…

U盘无法格式化怎么办?

U盘是我们日常工作和生活中经常使用的便携数据存储设备&#xff0c;它可以轻松地存储和传输文件&#xff0c;是工作和学习的好帮手。然而&#xff0c;有时候我们会遇到U盘无法格式化的情况&#xff0c;很多人可能会对此问题感到手足无措。本期内容将为大家详细解析U盘无法格式化…

0601 模拟集成电路中的直流偏置技术

模拟集成电路中的直流偏置技术 6.1.1 BJT 电流源6.1.2 FET电流源6.1.3电流源做作有源负载![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/c5381fff64bd48a1b28ba9bee179b18f.png) 6.1.1 BJT 电流源 6.1.2 FET电流源 6.1.3电流源做作有源负载

「实战应用」如何用图表控件LightningChart JS创建SQL仪表板应用(二)

LightningChart JS是Web上性能特高的图表库&#xff0c;具有出色的执行性能 - 使用高数据速率同时监控数十个数据源。 GPU加速和WebGL渲染确保您的设备的图形处理器得到有效利用&#xff0c;从而实现高刷新率和流畅的动画&#xff0c;常用于贸易&#xff0c;工程&#xff0c;航…

CorelDRAW2024永久破解版下载安装全教程!

在设计领域&#xff0c;精准和专业是至关重要的要素。随着技术的飞速发展&#xff0c;设计师们对软件的选择也越发严苛。CorelDRAW 2024中文版及其2024终身永久版、破解版&#xff0c;因其强大的功能和便捷的使用体验&#xff0c;成为了设计师们的首选之一。本文将深入探讨这一…

Blender 学习笔记(四)修改器

1. 概述 1.1 修改器是什么 如下图所示&#xff0c;我们可以给物体添加修改器。 修改器提供了很多操作&#xff0c;都是物体编辑模式所具有的&#xff0c;例如倒角、增加厚度、列阵等等&#xff0c;我们在添加修改器后&#xff0c;可以通过 ctrla 将这些修改应用。 1.2 为什…

微信小程序请求request封装

公共基础路径封装 // config.js module.exports {// 测试BASE_URL: https://cloud.chejj.cn,// 正式// BASE_URL: https://cloud.mycjj.com };请求封装 // request.js import config from ../config/baseUrl// 请求未返回时的loading const showLoading () > wx.showLoadi…

18_Vue3路由机制

Vue3路由机制router 1 路由简介 &#xff1a;路由就是根据不同的 URL 地址展示不同的内容或页面。 通俗理解&#xff1a;路由就像是一个地图&#xff0c;我们要去不同的地方&#xff0c;需要通过不同的路线进行导航。 2 路由的作用 单页应用程序&#xff08;SPA&#xff09;中…

GlusterFS企业分布式存储

GlusterFS 分布式文件系统代表-nfs常见分布式存储Gluster存储基础梳理GlusterFS 适合大文件还是小文件存储&#xff1f; 应用场景术语Trusted Storage PoolBrickVolumes Glusterfs整体工作流程-数据访问流程GlusterFS客户端访问流程 GlusterFS常用命令部署 GlusterFS 群集准备环…