C++ 引用详解

news2024/11/13 15:04:45

1.引用的基本语法

(1)语法形式:

数据类型 &别名 = 原名

(2)原理
给一个已有的变量起别名
在这里插入图片描述

int main()
{

	int a = 10;
	int& b = a;

	cout << "a=" << a << " b=" << b << endl;
	return 0;
}

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

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

	cout << "a=" << a << " b=" << b << endl;

	b = 100;
	cout << "a=" << a << " b=" << b << endl;
	return 0;

}

在这里插入图片描述
a和b访问的时同一块内存。引用的本质就是给一个变量起别名。

2.引用的注意事项

(1)引用必须初始化

int &b;//错误

(2)引用在初始化后,不可以再改变

在这里插入图片描述

int main()
{

	//引用的定义
	int a = 10;
	int& b = a;

	int c = 20;

	b = c;//不是更改引用而是赋值操作
	//上面这行代码的意义并不是把b作为c的别名,而是将c的值赋值给b
	cout << "a=" << a << " b=" << b << "  c=" << c << endl;

	return 0;

}

运行结果:
在这里插入图片描述
分析:
c本身的值就是20,没有发生改变;b=c;相当于把b指向的那块内存赋值为20,所以b的值为20;由于b是a的别名,b和a指向的是同一块内存,所以a的值为20。

3.引用做函数参数

调用函数的传参的方式一共有三种:值传递、地址传递和引用传递

(1)值传递
不可以通过形参修改实参
(2)地址传递
可以通过形参修改实参
(3)引用传参
可以通过形参修改实参,简化了指针修改实参。通过引用参数产生的效果和地址传递是一样的,引用语法更简单。

三种传递就是不一样的赋值结果。值传递,形参复制实参值;值地址传递,形参存入实参的地址;引用传递,将实参重命名为形参。

【示例】
(1)值传递

void myswap(int x, int y)
{
	int temp = x;
	x = y;
	y = temp;
	cout << "x=" << x << "   y=" << y << endl;
}
int main()
{
	int a = 10;
	int b = 20;
	myswap(a, b);
	cout << "a=" << a << "   b=" << b << endl;
	return 0;
}

运行结果:
在这里插入图片描述分析:
根据输出结果可以看出,主函数中的实参a和b并没有发生改变,但是被调函数中的形参的值发生了改变。所以值传递时,形参的改变并不会引起实参的改变,即不可以通过形参修改实参。

(2)地址传递

void myswap1(int *x, int *y)
{
	int temp = *x;
	*x = *y;
	*y = temp;
	cout << "x=" << *x << "   y=" << *y << endl;
}
int main()
{
	int a = 10;
	int b = 20;
	//myswap(a, b);
	myswap1(&a, &b);
	cout << "a=" << a << "   b=" << b << endl;
	return 0;
}

运行结果:在这里插入图片描述
分析:
在主函数中,myswap1(&a, &b);这条语句的意思是把a和b的内存的地址分别传给了myswap函数的形参x和y,所以在函数myswap中修改的数据就是外侧主函数中a和b的数据。所以,地址传递,形参的改变会影响实参,即可以通过形参去修改实参。

(3)引用传参

void myswap2(int& x, int& y)
{
	int temp = x;
	x = y;
	y = temp;
	cout << "x=" << x << "   y=" << y << endl;
}

int main()
{
	int a = 10;
	int b = 20;
	//myswap(a, b);
	//myswap1(&a, &b);
	myswap2(a, b);
	cout << "a=" << a << "   b=" << b << endl;
	return 0;
}

运行结果:
在这里插入图片描述
分析:
主函数中,在调用函数myswap2(a, b);时,myswap2函数的形参x就是主函数中实参a的一个别名,形参y就是主函数中实参b的一个别名,所以在myswap2函数中对形参x和y的任何操作和修改实际上就是对实参a和b的操作和修改。根据结果可以看出,引用传递的形参的改变也会影响实参,即可以通过形参去改变实参。

4.引用做函数返回值

(1)不要返回局部变量的引用

int& test1()
{
	int a = 10;//存放在栈区
	return a;
}
int main()
{
	int& ref = test1();
	cout <<"ref=" << ref << endl;//第一次结果正确
	cout << "ref=" << ref << endl;//第二次错误
	return 0;
}

代码分析:
test1函数以引用的方式作为返回值,并在函数体中返回局部变量a的值,相当于a有一个别名,把她的别名返回,这个别名在主函数中定义的一个引用ref来接收它。

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

结果分析:
局部变量a在test1函数运行结束之后就应该消失掉了,之所以第一次可以输出test1函数的局部变量a的值,是因为编译器做了保留。因为a的内存以及释放掉了,其实这块内存我们以及没有权利去操作了,所以第二次的结果是一个随机值,是错误的。

所以不要返回局部变量的引用,如果返回的是局部变量的引用,那么在函数外侧再用别名去操作这段内存已经是非法操作了。
(2)函数的调用可以作为左值

int& test2()
{
	static int a = 10;//该变量存放在全局区,全局区的数据在程序结束后由系统释放
	return a;
}
int main()
{
	//int& ref = test1();
	//cout <<"ref=" << ref << endl;//第一次结果正确
	//cout << "ref=" << ref << endl;//第二次错误

	int &ref2 = test2();
	cout << "ref2=" << ref2 << endl;
	cout << "ref2=" << ref2 << endl;

	test2() = 1000;//如果函数的返回值是一个引用,这个函数的调用可以作为左值。

	cout << "ref2=" << ref2 << endl;
	cout << "ref2=" << ref2 << endl;

	return 0;
}

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

分析:
函数test2调用完之后返回的是静态变量a的一个引用,把a变量返回,主函数中的 test2() = 1000;就相当于做了一个a=1000;的操作,用原名a赋值1000,test2本身就是初始化a这个返回值的别名。因为test2本身返回的就是一个a的引用,在 test2() = 1000;这条语句执行完之后,因为主函数中的ref2就是a的一个别名,所以无论是用test2调用完之后给它赋值1000,还是让静态变量a等于1000,或者使用别名ref2去访问这块内存,都可以。

所以如果函数的返回值是一个引用,这个函数的调用可以作为左值。

4.引用的本质

引用的本质在C++内部实现是一个指针常量

void fun(int& b)
{
	b = 100;
}

int main()
{
	int a = 10;

	int& b = a;
	b = 20;

	cout << "a=" << a << endl;
	cout << "b=" << b << endl;

	fun(a);

	return 0;

}

运行结果:
在这里插入图片描述
结果分析:
在主函数中创建了一个整型数据a并初始化为10,在程序执行到int& b = a;这行代码时,系统编译器会自动将其转换为int *const b=&a;,这时候就相当于定义了一个指针常量,指向a的地址,这里const修饰的是指针的指向,说明指针的指向是固定的不可以修改,这也就说明为什么引用不可以更改。但是指针常量指向的内容可以修改。再继续执行到b = 20;这一行代码的时候,内部发现b是一个引用,会自动帮我们将其转换为*b=20;。所以输出a和b的值其实都是a的内存单元中存放的值。主函数中最后的 fun(a);这行代码,调用函数fun,将a作为实参传给fun函数的形参,fun函数中对应的形参为引用的方式int& a,这里发现它是引用,就会自动转换为int *const b=&a;,同理fun函数体中的b=100;这行代码也会自动转换为*b=100;

引用的本质就是一个指针常量。 所以引用一旦初始化之后就不可以发生改变。C++推荐使用引用技术,因为语法方便,引用的本质是指针常量,但是所有的指针操作编译器都帮我们做了。

5.常量引用

(1)使用场景

修饰形参

(2)作用

常量引用主要用来修饰形参,防止误操作。

在函数的形参列表中,可以加const修饰,防止形参改变实参。

(3)示例

【例1】

int main()
{
	int a = 10;
	//int& b = 10;//错误  10是一个常量,引用必须引用的是内存上的合法内存空间

	const int& b = 10;//正确
	//这个引用,引用的是一块临时的空间,我们想要操作它的时候找不到它的原名,我们只能用这个别名去操作它
	//加上const之后,编译器将代码修改为  int temp=10;  int&b=temp;

	//b = 20;//错误,加了const之后变为只读,不可以修改

}

分析:
①引用不可以指向一个常量,引用必须引用的是内存上的合法内存空间
②如果一个引用指向常量并用const来修饰,那么就是正确的,加了const之后就相当于编译器自动创建了一个临时变量来存放这个常量。加了const之后引用就使只读,不可修改。

【例2】

//打印数据的函数
void Show(int &s)
{
	s = 1000;//修改形参s的值
	cout << "形参s=" << s << endl;
}
//主函数
int main()
{
	int a = 100;//创建一个变量

	Show(a);//调用Show函数把a以引用的方式传入

	cout << "实参a=" << a << endl;
	
	return 0;
}

运行结果:
在这里插入图片描述
结果分析:
在打印数据函数Show中,不小心或者不经意修改了形参s的值,而主函数中的实参a也被同步修改了,因为s是a的一个别名,所以修改了别名s的值就相当于修改了a的值,但是在主函数中的a并不想被修改。代码比较少的时候我们会发现不小心修改了形参的值,但是如果代码非常多,我们不容易发现,并且忘了Show函数一开始只是打印数据的功能,那么就会输出错误的结果,不容易找到出错原因。为了防止这个误操作,我们就在形参的前面加一个const来修饰它:void Show(const int &s),这时候去修改Show函数中的形参s的值就会编译不通过:
在这里插入图片描述
这时,我们就不能在被调函数的函数体内修改形参的值,也就不会使主调函数中的实参被修改而发生误操作。

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

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

相关文章

Vector - CAPL - 诊断模块函数(流控制帧)

目录 CanTpIsUseFlowControlBlockSize & CanTpUseFlowControlBlockSize 代码示例 CanTpGetOneFlowControlValue & CanTpSetOneFlowControlValue 代码示例 CanTpGetAckMode & CanTpSetAckMode & CanTpGetAckResult & CanTpSetAckResult 代码示例 CanT…

MySQL5.7数据库、Navicat Premium1.6可视化工具安装教程【详细教程】

文章目录 一、MySQL、Navicat、注册机地址二、安装&#xff08;一&#xff09;、MySQL安装&#xff08;二&#xff09;、Navicat Premium安装&#xff08;三&#xff09;、集活Navicat Premium 三、遇到的问题1、Are you sure your navicat has not beenpatched/modified befor…

android开发之Android 自定义滑动解锁View

自定义滑动解锁View 需求如下&#xff1a; 近期需要做一个类似屏幕滑动解锁的功能&#xff0c;右划开始&#xff0c;左划暂停。 需求效果图如下 实现效果展示 自定义view如下 /** Desc 自定义滑动解锁View Author ZY Mail sunnyfor98gmail.com Date 2021/5/17 11:52 *…

测评HTTP代理的透明匿名?

在我们日常的网络冒险中&#xff0c;你是否曾听说过HTTP代理的透明匿名特性&#xff1f;这些神秘的工具就像是网络世界中的隐身斗士&#xff0c;让我们能够在互联网的迷雾中保护自己的身份和隐私。那么&#xff0c;让我们一起揭开HTTP代理的面纱&#xff0c;探索其中的奥秘吧&a…

opencv安装报错解决方案

菜鸟程序员写代码5分钟&#xff0c;配环境5小时 这里记录一下opencv配置报错&#xff0c;其实之前碰到过很多遍了 情况1&#xff1a;安装的时候卡在这一块 Building wheel for opencv-python (pyproject.toml) 解决方案&#xff1a;在安装指令后加--verbose pip install o…

并发三大特性和JMM

一、并发三大特性 1、原子性 一个或多个操作&#xff0c;要么全部执行且在执行过程中不被任何因素打断&#xff0c;要么全部不执行。在Java中&#xff0c;对基本数据类型的读取和赋值操作是原子性操作&#xff08;64位处理器&#xff09;。不采取任何的原子性保障措施的自增操…

微信小程序 map地图(轨迹)

allMarkers效果图 废话少说直接上马&#xff08;最后是我遇到的问题&#xff09; cover-view是气泡弹窗&#xff0c;可以自定义弹窗&#xff0c;要配合js&#xff1a;customCallout&#xff0c;如果是非自定义的话&#xff1a;callout&#xff08;可以修改颜色、边框宽度、圆角…

《Zookeeper》源码分析(五)之 ServerCnxnFactory的工作原理(上)

目录 AcceptThread数据结构构造函数run() SelectorThread数据结构processAcceptedConnections()select()processInterestOpsUpdateRequests() 本文开始分析 ServerCnxnFactory的工作原理&#xff0c;按照顺序我们这样分析&#xff1a; 建立连接监听读写事件处理读写就绪的事件…

【图像去噪的滤波器】非局部均值滤波器的实现,用于鲁棒的图像去噪研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Selenium 自动化测试实战笔记1

1. 安装 selenium pip install selenium 3.11.0 # 安装指定版本 pip install selenium -U # 安装最新版本 pip show selenium # 查看当前版本 pip uninstall selenium # 卸载 报错解决1&#xff1a; 带上代理 pip install selenium -i http://mirrors.aliyun.com/…

C++项目:在线五子棋对战(网页版)

项目介绍 本项⽬主要实现⼀个⽹⻚版的五⼦棋对战游戏&#xff0c;其主要⽀持以下核⼼功能&#xff1a; • 用户管理:实现用户注册&#xff0c;用户登录、获取用户信息、用户天梯分数记录、用户比赛场次记录等。 • 匹配对战:实现两个玩家在网页端根据天梯分数匹配游戏对⼿&…

图的遍历之 深度优先搜索和广度优先搜索

深度优先搜索的图文介绍 1. 深度优先搜索介绍 图的深度优先搜索(Depth First Search)&#xff0c;和树的先序遍历比较类似。 它的思想&#xff1a;假设初始状态是图中所有顶点均未被访问&#xff0c;则从某个顶点v出发&#xff0c;首先访问该顶点&#xff0c;然后依次从它的各…

亚马逊 EC2服务器下部署java环境

1. jdk 1.8 安装 1.1 下载jdk包 官网 Java Downloads | Oracle tar.gz 包 下载下来 1.2 本地连接 服务器 我用的是亚马逊的ec2 系统是 ubuntu 的 ssh工具是 Mobaxterm , 公有dns 创建实例时的秘钥 链接 Mobaxterm 因为使用的 ubuntu 所以登录的 名称 就是 ubuntu 然后 …

Leetcode每日一题:2681. 英雄的力量(2023.8.1 C++)

目录 2681. 英雄的力量 题目描述&#xff1a; 实现代码与解析&#xff1a; 数学规律 原理思路&#xff1a; 2681. 英雄的力量 题目描述&#xff1a; 给你一个下标从 0 开始的整数数组 nums &#xff0c;它表示英雄的能力值。如果我们选出一部分英雄&#xff0c;这组英雄的…

【kubeadm的配置安装】

目录 一、环境准备二、所有节点安装docker三、部署K8S集群1、查看镜像2、初始化kubeadm方法一&#xff1a;1、修改配置文件2、在线拉取镜像3、初始化 master 方法二、 3、设定kubectl4、所有节点部署网络插件flannel 四、部署 Dashboard1、在 master01 节点上操作 master&#…

分布式事务面试题

一、事务简介 事务(Transaction)是操作数据库中某个数据项的一个程序执行单元(unit)。 事务应该具有4个属性&#xff1a;原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性。 1.1、名词解释 事务&#xff1a;事务是由一组操作构成的可靠的独立的工作单元&#x…

【前端】Vue生命周期函数(详细讲解+中文图解)

目录 一、何为生命周期1、含义2、理解 二、生命周期定义&#xff08;官网&#xff09;1、vue22、vue3 三、生命周期图解1、vue2生命周期图解2、vue3生命周期图解 四、Vue的生命周期五、Vue2生命周期和Vue3生命周期的区别六、Vue生命周期的主要阶段以及8个周期函数1、options AP…

BM5 合并k个已排序的链表 javascript

描述 合并 k 个升序的链表并将结果作为一个升序的链表返回其头节点。 数据范围&#xff1a; 示例1 输入&#xff1a; [{1,2,3},{4,5,6,7}] 返回值&#xff1a; {1,2,3,4,5,6,7}示例2 输入&#xff1a; [{1,2},{1,4,5},{6}] 返回值&#xff1a; {1,1,2,4,5,6}解题思路 利用两个…

Python numpy中的correlate相关性详解

看代码看见这个方法&#xff0c;记录一下&#xff0c;这个是人家官网的链接np.correlate 云里雾里的&#xff0c;其实就是两个数组点乘&#xff0c;不同模式就是错位点乘&#xff0c;直接看代码 a是原本的数组&#xff0c;v就是滤波器&#xff0c;对应相乘 import numpy as …

分布式 - 消息队列Kafka:Kafka生产者发送消息的3种方式

文章目录 1. Kafka 生产者2. kafaka 命令行操作3. Kafka 生产者发送消息流程4. Kafka 生产者发送消息的3种方式1. 发送即忘记2. 同步发送3. 异步发送 5. Kafka 消息对象 ProducerRecord 1. Kafka 生产者 Kafka 生产者是指使用 Apache Kafka 消息系统的应用程序&#xff0c;它们…