学习C++第三天——对引用的深入了解

news2024/11/24 1:16:37

引用

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空 间,它和它引用的变量共用同一块内存空间。


一个变量可以有多个引用:

int main() {
	//一个变量可以有多个引用
	int a = 10;
	int& b = a;
	int& c = a;
	int& d = a;
	return 0;
}

引用相当于给这个变量取别名,在西游记中孙悟空是孙悟空,齐天大圣也是孙悟空,弼马温也是孙悟空,孙行者也是孙悟空,斗战胜佛也是孙悟空,这些别名都是在指同一个猴子。

C++中的引用就相当于在给引用实体取别名


引用在定义的时候必须初始化:

对引用不初始化是会报错的

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


引用一旦引用实体,就不能再引用其他实体:

int main() {
	int a = 10;
	int& b = a;
	int c = 20;
	b = c;
	cout << "a=" << a<<endl;
	cout << "b=" << b << endl;
	cout << "c=" << c << endl;
	return 0;
}
//输出结果a=20,b=20,c=20

b是a的别名,将c赋值给b,就相当于给a赋值,所以a,b,c的值都是20

所以引用一旦引用实体后就不能引用其他实体,否知就是将其他实体的内容拷贝到引用里面


引用做参数:

交换两个数

void swap(int& x, int& y) {
	int tmp = x;
	x = y;
	y = tmp;
}

在C语言中交换两个参数需要用到指针,比较麻烦,这里使用引用就比较方便

当引用做参数时,实参传过来,相当于x,y就是实参的别名,对引用进行修改也就是对实参进行修改

私货:

typedef struct ListNode {
	int val;
	struct nextNode* next;
}*PNode;

void ListPushBack(PNode& phead, int x);

定义一个链表的节点,重定义节点指针类型为PNode,用PNode类型做引用的类型,所以改变phead就可以改变其里面的内容。

传值和传引用比较效率:

#include<iostream>
using namespace std;

struct A {int arr[10000];};//定义一个字节数比较大的结构体

void mothed1(A a) {};//传值的方法
void mothed2(A& a) {};//传引用的方法

void test() {
	A a;//创建一个结构体
	size_t begin1 = clock();//记录传值方法开始的时间
	for (size_t i = 0; i < 10000; i++)//进行10000次传值
	{
		mothed1(a);
	}
	size_t end1 = clock();//记录传值结束的时间

	size_t begin2 = clock();//记录传引用开始的时间
	for (size_t i = 0; i < 10000; i++)//进行10000次传引用
	{
		mothed2(a);
	}
	size_t end2 = clock();//记录传引用结束的时间

	cout << end1 - begin1 << endl;//计算传值所花费的时间
	cout << end2 - begin2 << endl;//计算传引用所花费的时间
}

int main() {
	test();//调用测试函数
}//结果为6,0

显然传引用的效率高于传值


引用做返回值:

传值返回:

 当返回值不是引用时,编译器都会生成临时变量

不管n时是否是static修饰的还是其他别的类型,只要是传值返回都要生成临时变量

传引用返回:

减少拷贝,提高效率,不会生成临时变量

struct A { int arr[10000]; };//定义一个结构体
A a;//创建一个结构体
A mothed1() { return a; };//实现一个传值返回函数
A& mothed2() { return a; };//实现一个传引用返回函数

void test() {
	
	size_t begin1 = clock();//获取开始时间
	for (size_t i = 0; i < 10000; i++)//调用10000传值返回函数
	{
		mothed1();
	}
	size_t end1 = clock();//获取结束时间

	size_t begin2 = clock();//获取开始时间
	for (size_t i = 0; i < 10000; i++)//调用10000次传引用返回函数
	{
		mothed2();
	}
	size_t end2 = clock();//获取结束时间

	cout << end1 - begin1 << endl;//计算传值返回所用的时间
	cout << end2 - begin2 << endl;//计算传引用返回所用的时间
}

int main() {
	test();
}
//输出结果为18,0

传值返回和传引用返回有很大差异,传引用返回的效率远高于传值返回

传引用返回的问题:

int& fun() {
	int n = 10;
	n++;
	return n;
}

int main() {
	int ret=fun();
	cout<< ret;
	return 0;
}

当引用作为返回值时,没有临时变量,提高了效率。

当引用返回值用整形接收时,是将引用的内容拷贝到ret里面

当fun函数结束时,fun的函数栈帧会被销毁,也就是失去了访问权限

当ret指向的栈帧没有被清理时,结果不变

当ret指向的栈帧被清理后,结果为随机值

ret指向的空间没有保障

当传引用返回用引用接收:

int& fun() {
	int n = 10;
	n++;
	return n;
}

int main() {
	int &ret=fun();
	cout<< ret<<endl;
	printf("<<<<<<<<<<<<<\n");
	rand();
	cout << ret;
	return 0;
}

当返回值用引用接收时,ret指向引用的那块空间,在不被清理的时候,ret的值侥幸正确,当被清理时,就会ret的值就会变成随机值,就算没被清理,当调用其他函数时,栈帧会被覆盖,那就毫无疑问的变成随机值了

返回引用的实体是被static修饰的:

int& fun() {
	static int n = 10;
	n++;
	return n;
}

int main() {
	int &ret=fun();
	cout<< ret<<endl;
	printf("<<<<<<<<<<<<<\n");
	rand();
	cout << ret;
	return 0;
}

这时候使用引用返回是安全的

因为这时候的n是在静态区,fun结束,函数栈帧销毁对静态区的n没有影响

对引用的扩充使用:

typedef struct SeqList
{
	int a[10];
	size_t size;
}SqList;//定义一个简易的静态顺序表

int SLGet(SeqList* ps, int pos) {//获取对应下标的值
	assert(pos >= 0 && pos < 10);
	return ps->a[pos];
}

void Modify(SeqList* ps, int pos, int val) {//修改对应下标的值
	assert(pos >= 0 && pos < 10);
	ps->a[pos] = val;
}

int main() {
	SeqList s;
	Modify(&s, 0, 1);
	cout << SLGet(&s, 0)<<endl;

	int ret = SLGet(&s, 0);
	Modify(&s, 0, ret + 5);
	cout << SLGet(&s, 0) << endl;

	return 0;
}

这样用c语言的指针写比较麻烦


#include<assert.h>

typedef struct SeqList
{
	int a[10];
	size_t size;
}*SeqList;


int& SLAt(SeqList& ps, int pos) {
	assert(pos >= 0 && pos < 10);
	return ps->a[pos];
}

int main() {
	SeqList s;
	SLAt(s, 0) = 10;
	cout << SLAt(s, 0) << endl;
	SLAt(s, 0)++;
	cout << SLAt(s, 0) << endl;
	SLAt(s, 0) -= 3;
	cout << SLAt(s, 0) << endl;
}

引用做返回值有获取和修改返回值的功能,就可以实现指针的功能

#include<iostream>
#include<assert.h>
using namespace std;

struct  SeqList
{
	int a[100];
	size_t size;
	int& at(int pos) {
		assert(pos >= 0 && pos < 100);
		return a[pos];
	}

	int& operator[](int pos) {
		assert(pos >= 0 && pos < 100);
		return a[pos];
	}

};

int main() {
	SeqList s;
	s.at(0) = 10;
	s.at(0)++;
	cout << s.at(0)<<endl;

	s[0] = 20;
	s[0]++;
	cout << s[0] << endl;

	return 0;
}

常引用:

1.权限不能扩大

int main() {
	//权限不能扩大
	const int a = 10;
	//int& b = a;
	const int& c = a;
}

a是const修饰变量,不能被修改,所以他的别名也必须被const修饰,保证权限不被扩大

2.const修饰的变量可以赋值

int main() {
	const int a = 10;
	int b = a;
}

const修饰的a相当于常量,不能被修改,但是可以赋值,相当于将a的内容拷贝到b里面

3.引用过程中权限可以缩小和偏移

int main() {
	int a = 10;
	int& b = a;//权限平移
	const int& c = a;//权限缩小
	b++;
	//c++;
}

上述权限平移可以执行原来的修改,但是权限缩小就不能修改了

int& fun1() {
	static int x = 10;
	return x;
}

int main() {
	int& ret = fun1();//权限的平移
	const int& ret1 = fun1();//权限的缩小
	return 0;
}

ret和ret1均为x的别名

4.给常量取别名

int main() {
	const int& a = 10;
}

a是不能被修改的

5.隐式类型转化

 

 

6.临时变量具有常属性

所以ret需要const修饰

引用和指针的不同点:

1. 引用概念上定义一个变量的别名,指针存储一个变量地址。

2. 引用在定义时必须初始化,指针没有要求

3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何 一个同类型实体

4. 没有NULL引用,但有NULL指针

5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32 位平台下占4个字节)

6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小

7. 有多级指针,但是没有多级引用

8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理

9. 引用比指针使用起来相对更安全

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

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

相关文章

ubuntu的不同python版本的pip安装及管理

ubuntu的不同python版本的pip安装及管理_ubuntu 安装两个pip-CSDN博客https://blog.csdn.net/qq_32277533/article/details/106770850

老板舍不得买库存管理软件❓一招解决

在当今快节奏的商业环境中&#xff0c;仓库管理是企业运作中不可或缺的一环。对于许多中小型企业而言&#xff0c;简易且高效的库存管理系统尤为重要。搭贝简易库存管理系统针对仓库的出入库进行有效管理&#xff0c;帮助企业实现库存的透明化和流程的自动化。 客户的痛点 1. …

Java面试八股之Mybatis可以映射到枚举类吗

Mybatis可以映射到枚举类吗 Mybatis 可以映射到 Java 的枚举类型。默认情况下&#xff0c;Mybatis 会使用枚举类型的名称来进行映射。例如&#xff0c;如果你有一个如下的枚举类型&#xff1a; public enum UserStatus { ACTIVE, INACTIVE } Mybatis 会将数据库中的字符串值…

【Linux】进程间通信_4

文章目录 七、进程间通信1. 进程间通信分类systeam V共享内存消息队列信号量 未完待续 七、进程间通信 1. 进程间通信分类 systeam V共享内存 进程间通信的本质就是让不同进程看到同一份资源。而systeam V是通过让不同的进程经过页表映射到同一块内存空间&#xff08;操作系…

强化学习-RLHF-PPO入门

一、定义 强化学习微调分类RM模型 数据集格式训练流程Reward 模型训练流程(分类模型&#xff0c;积极为1&#xff0c;消极为0) AutoModelForSequenceClassificationReward 模型训练案例PPO模型训练流程PPO模型训练案例 二、实现 强化学习微调分类 RLHF:基于人类反馈对语言模型…

深度学习 —— 1.单一神经元

深度学习初级课程 1.单一神经元2.深度神经网络3.随机梯度下降法4.过拟合和欠拟合5.剪枝、批量标准化6.二分类 前言 本套课程仍为 kaggle 课程《Intro to Deep Learning》&#xff0c;仍按之前《机器学习》系列课程模式进行。前一系列《Keras入门教程》内容&#xff0c;与本系列…

eNSP中三层交换机的配置和使用

一、拓扑图 1.新建拓扑图 2.PC端配置 PC1: PC2&#xff1a; 二、基本命令配置 1.S1配置 <Huawei>system-view [Huawei]sysname S1 [S1]vlan 10 //在交换机 S1 上创建 VLAN 10 [S1-vlan10]vlan 20 // 在交换机 S1 上创建 VLAN 20 [S1-vlan20]quit //退出 VLAN 配置…

基于JSP的在线教育资源管理系统

开头语&#xff1a; 你好呀&#xff0c;我是计算机学长猫哥&#xff01;如果你对在线教育资源管理系统感兴趣或者有相关需求&#xff0c;欢迎在文末找到我的联系方式。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;JSP技术 工具&#xff1a;IDE、N…

基于Java仓储出入库管理系统设计和实现(源码+LW+调试文档+讲解等)

&#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f;感兴趣的可以先收藏起来&#xff0c;还…

代码随想录第34天|贪心算法

59.合并区间 class Solution { public:struct cmp{bool operator()(vector<int>& a, vector<int>& b) {return a[0] < b[0];}};vector<vector<int>> merge(vector<vector<int>>& intervals) {if (intervals.size() 1)retu…

六款顶级原型设计工具推荐,满足你所有需求!

即时设计作为一款专业原型工具&#xff0c;无论是从功能还是插件库配备情况来看&#xff0c;都是毫无疑问可以进行原型图设计的&#xff0c;而且&#xff0c;即时设计内设海量资源库&#xff0c;可以支持大家通过关键词进行搜索相关资源&#xff0c;并且在线编辑使用&#xff0…

Zookeeper:分布式系统中的协调者

Zookeeper&#xff1a;分布式系统中的协调者 前言&#xff1a;引言Zookeeper是什么&#xff1f; 基本概念Zookeeper 数据模型Znode 类型会话Watcher 应用场景分布式锁配置维护组服务名字服务 典型应用场景数据发布/订阅负载均衡命名服务分布式协调/通知集群管理Master选举 工作…

Open3D kitti数据集bin与pcd的相互转换

目录 一、Kitti数据集简介 1.1数据集内容 1.2数据集结构 二、代码实现 2.1bin转pcd 2.2pcd转bin 三、实现效果 一、Kitti数据集简介 KITTI 数据集是由德国卡尔斯鲁厄理工学院&#xff08;KIT&#xff09;和丰田美国技术研究院&#xff08;Toyota Technological Institut…

昇思25天学习打卡营第2天|快速入门

使用MindSpore实现简单的深度学习模型 环境配置 第一步当然是装包&#xff1a; !pip install -i https://pypi.mirrors.ustc.edu.cn/simple mindspore2.2.14 import mindspore from mindspore import nn from mindspore.dataset import vision, transforms from mindspore.d…

深入了解MySQL的哈希索引

深入了解MySQL的哈希索引 哈希索引是一种基于哈希表的数据结构&#xff0c;通过对索引键值进行哈希运算&#xff0c;直接定位存储位置&#xff0c;从而实现快速数据访问。哈希索引在等值查询中表现尤为出色&#xff0c;但不适用于范围查询。虽然哈希索引在某些场景下可以显著提…

从特斯拉视角,看智能驾驶研究框架

第一章:回顾历史&#xff0c;智能驾驶的核心主线是算法的演进史&#xff0c;从2017年至今在感知侧规控侧实现算法从规则为主走向端到端。算法方面&#xff0c;2017-2022年&#xff0c;特斯拉在感知侧走向端到端&#xff0c;实现BEVTransformerOccupancy。2021-2023年&#xff0…

Python深度学习技术

原文链接&#xff1a;Python深度学习技术 近年来&#xff0c;伴随着以卷积神经网络&#xff08;CNN&#xff09;为代表的深度学习的快速发展&#xff0c;人工智能迈入了第三次发展浪潮&#xff0c;AI技术在各个领域中的应用越来越广泛。Transformer模型&#xff08;BERT、GPT-…

2024年最新中级会计职称考试题库。

46.甲将一汇票背书转让给乙&#xff0c;但该汇票上未记载乙的名称。其后&#xff0c;乙在该汇票被背书人栏内记载了自己的名称。根据《票据法》的规定&#xff0c;下列有关该汇票背书与记载效力的表述中&#xff0c;正确的是&#xff08;&#xff09;。 A.甲的背书无效&#x…

C语言:sprintf与snprintf

C语言提供了强大的格式化输出的接口&#xff0c;可以输出到不同的文件或者字符串等&#xff0c;以sprintf和snprintf为例介绍一下 sprintf 格式化输出到字符串 函数签名 int sprintf(char *str, const char *format, ...);与printf相比就是多了前面的char*参数&#xff0c;…

创新降重工具助力学术写作:提升论文独创性

现在大部分学校已经进入到论文查重降重的阶段了。如果查重率居高不下&#xff0c;延毕的威胁可能就在眼前。对于即将告别校园的学子们&#xff0c;这无疑是个噩梦。四年磨一剑&#xff0c;谁也不想在最后关头功亏一篑。 查重率过高&#xff0c;无非以下两种原因。要么是作为“…