c++|引用

news2024/9/26 5:16:53

目录

一、引用概念

二、引用特性 

三、常引用 (具有常属性的引用变量)

 四、使用场景


一、引用概念

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

比如:李逵,在家称为“铁牛”,江湖上人称“黑旋风”

那么引用变量的形式是什么

 类型& 引用变量名(对象名) = 引用实体;

#include <iostream>
using namespace std;


void TestRef()
{
	int a = 10;
	int& ra = a;//定义引用类型,ra仅仅只是a的别名,并没有给ra开空间
	//注意:在定义引用时,必须给引用变量名初始化--语法规定(祖师爷的规定)
	printf("%p\n", &a);//打印地址也是没有区别的
	printf("%p\n", &ra);
}

int main()
{
	
	TestRef();

	return 0;
}

 运行结果:

二、引用特性 

1.引用在定义时必须初始化

2.一个变量可以有多个引用

3.引用一旦引用一个实体,再不能引用其他实体(就是只能定义一次嘛)


void TestRef()
{
	int a = 10;
	int& ra = a;//定义引用类型,ra仅仅只是a的别名,并没有实质的开空间
	//注意:在定义引用时,必须给引用变量名初始化,语法规定(祖师爷的规定)
	//int &ra;//像这样直接定义引用,而不初始化就会报错

	int& rra = a;//一个变量可以有多个引用,ra和rra都是a的引用

	printf("%p\n", &a);
	printf("%p\n", &ra);
	printf("%p\n", &rra);
}

int main()
{
	
	TestRef();

	return 0;
}

运行结果:

三、常引用 (具有常属性的引用变量)

#include <iostream>
using namespace std;


void TestRef()
{
	const int a = 10;//用const修饰变量a,具有常属性,不可直接修改
	//此时再用int &ra = a;会报错
	const int& ra = a;//必须加上const修饰才行,为什么,这里有点难理解
	
	//来看const int a = 10;和int &ra = a;这两句。a具有常属性,不可直接修改
	//将具有常属性的a赋值给引用变量,中间其实是会产生一个临时变量,临时变量是自带常属性的,临时变量要赋值给引用变量
    //而引用变量的类型为int&,引用的实体应该是对应可修改变量的类型,所以将具有常属性的临时变量赋值给可修改类型的引用变量是不可行的。
    //这就是一个权限放大的过程,是不行的
	//注意:权限可以平移或者缩小,但不能被放大

	//那再来看const int a = 10;和 const int& ra = a; a具有常属性,引用变量也具有常属性,可以直接赋值,权限平移
    const int& aa = 10;//也是权限的平移,10是一个常量,具有常属性,引用变量也是具有常属性
	//那么再来看权限的缩小
	int b = 20;
	const int& rb = b;//b是可修改类型,而生成的临时变量是具有常属性的,引用变量类型也是具有常属性的,所以临时变量可以赋值给引用变量。
	//即由int类型(可修改类型)b转换成具有常属性的不可修改的临时变量,这是权限的缩小。也是OK的
}

int main()
{
	
	TestRef();

	return 0;
}

上面大费周章的说了一下常引用的用法是因为中间产生临时变量 ,那么临时变量是什么,因为什么原因产生的?

答:顾名思义临时变量是一种临时存在的变量,其实临时变量产生的原因是赋值时,两边变量类型的不一致产生的,且自带常属性,它存放在寄存器,由寄存器管理。

#include <iostream>
using namespace std;


void TestRef()
{
	//像下面这种赋值,就产生了临时变量
	int i = 1;
	double b = i;//i是int类型,b是double类型,i会生成临时变量,临时变量复制给了b

	double d = 12.5;
	//int& ra = d;编译会报错,因为引用变量的类型是int&,而d的类型是double,类型不一致
	//有人可能会有疑问,他们不会发生隐式转换吗?不会,隐式类型转换的前提是一组相近类型之间的转换
	//例如:int double float等等他们都是描述数据的相似类型,之间可发生隐式类型转换。而上述的引用类型与double不一致

	const int& rb = d;//像这个,double类型的d会先发生隐式类型转换成int,然后由int类型的d生成临时变量,临时变量具有常属性
	//可以赋值给具有常属性的引用变量

}

int main()
{
	
	TestRef();

	return 0;
}

 四、使用场景

1.做参数

#include <iostream>
using namespace std;

//在C++中,传参时可以用引用传参,就是给实参取个别名罢了,不会像C语言中的形参一样额外开空间
//好处就是减少了空间的开销
void Swap(int &left, int& right)
{
	int temp = left;
	left = right;
	right = temp;
}

int main()
{
	int a = 3, b = 4;
	Swap(a, b);//注意:这里如果传的是常量,那么引用时需加上const,理由在临时变量那说的很清楚了

	return 0;
}

2.做返回值 

先说结论:如果函数返回时,除了函数作用域,如果返回对象还在(还没还给系统),则可以使用引用返回,如果已经还给系统了,则必须使用传值返回。

来看分析这个代码: 

#include <iostream>
using namespace std;

int& count()//引用函数可以看做是给这个count函数的别名
{
	static int n = 0;//定义了一个静态变量,该对象存放在静态区,不会随着count函数的销毁而销毁
	//生命周期是整个main函数的生命周期,只有main函数销毁了才回销毁
	n++;
	return n;//这里有个细节:对于传值返回的函数在进行返回值时,并不会直接返回这个值,中间其实会产生一个临时变量
	//在函数销毁时,这个变量也就是销毁了,返回的实际值其实是这个临时变量的值,对于临时变量存放位置取决于该对象的大小
	// 如果比较小的话 4/8bit——>寄存器
	//如果比较大的话 —— > 临时变量放在上一个栈帧(调用他的栈帧中)
	// 
	//而对于传引用返回,并不会产生临时变量,返回的是这个值的别名
	//

	//这里使用了引用返回,且count函数销毁时,n对象并没有销毁,赋值给了引用函数count
}


int main()
{
	int& ret = count();//引用函数作为返回值给ret,其实就是相当于返回了n的别名
	count();
	cout << ret << endl;
	return 0;
}

运行结果:

再来对比一下这个代码:

#include <iostream>
using namespace std;

int& Add(int a, int b)
{
	int c = a + b;//作用域只限定在这个函数内部,函数销毁时,该变量也会销毁
	return c;//此时传引用返回,而c又随着函数的销毁而销毁了,那么此时引用函数的值就是随机值了
	//所以对于返回对象出了函数作用域,要还给系统的,必须使用传值返回
}

int main()
{
	int& ret = Add(1, 2);
	cout << "Add(1,2) is :" << ret << endl;
	return 0;
}

运行结果:

6.5 传值、传引用效率比较

对于以值作为传参的参数或者传值返回,并不会直接传递实参或者将变量本身直接返回,而是传递实参或者返回变量的一份临时的拷贝,对于传引用返回而言,是直接传回对象的别名,不需要进行拷贝。所以传值返回效率是非常低下的,尤其是对象很大时,还需要拷贝一份,效率更低。

例如:

#include <iostream>
using namespace std;

#include <time.h>
struct A { int a[10000]; };
void TestFunc1(A a){}

void TestFunc2(A& a) {}

void TestValuetime()
{
    A a;
    //以值作为参数
    size_t begin1 = clock();//获取当前时间
    for (size_t i = 0; i < 10000; i++)
    {
        TestFunc1(a);
    }

    size_t end1 = clock();//循环结束后,再获取当前时间

    //以引用作为参数
    size_t begin2 = clock();
    for (size_t i = 0; i < 10000; i++)
    {
        TestFunc2(a);
    }
    size_t end2 = clock();

    cout << "TestFunc1-time:" << end1 - begin1 << endl;
    cout << "TestFunc2-time:" << end2 - begin2 << endl;
}

int main() {
   
    TestValuetime();
    return 0;
}

运行结果:

通过代码比较,还是可以发现传值和引用在作为传参以及返回值类型上效率相差很大。

6.6引用和指针的区别

我们知道在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。 

其实在底层实现上实际是有空间的,引用是按照指针方式来实现的。我们写代码,相当于在上层的页面写代码,编译器进行编译时,是对上层的代码进行语法分析、语义分析、符号汇总等等,一定要区分语法概念和底层的区别,所以引用对于上层而言就是一个语法概念的存在

例子: 

#include <iostream>
using namespace std;

int main() {
   
    int a = 10;
    int& ra = a;
    ra = 20;

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

    return 0;
}

 该代码反汇编(先进入调试模式、右击代码,选中转反汇编):

 虽然他们在底层上实现是一样的,但在上层引用和指针在各方面还是有区别的:

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

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

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

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

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

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

7.有多级指针,但是没有多级引用(不管被引用多少次,始终是一个变量的别名)

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

9.引用比指针使用起来相对更安全(引用定义时就与变量绑定,也不用操作地址,而指针定义时未初始化就是野指针,还可以通过修改地址来访问变量)

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

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

相关文章

C++ 使用c++类模板实现动态数组-可实现自定义数据类型存储

.hpp文件 #include <iostream> #include <cstdlib> #include <cstring> using namespace std; template <class T> class arraylist { private:T* data ;//数组地址int size;//长度int count;//容量public:arraylist();~arraylist();void add(T t);T&…

革新突破!智能指标平台引领时代,国产大模型与企业级部署的完美结合

11月21日&#xff0c;跬智信息&#xff08;Kyligence&#xff09;圆满召开了线上数智论坛暨产品发布会&#xff0c;升级智能一站式指标平台 Kyligence Zen 及 AI 数智助理 Kyligence Copilot 的一系列企业级能力&#xff0c;包括正式支持智谱 AI、百川智能等在内的多款国产大模…

什么是数字化工厂?企业数字化转型有什么好处?

科技在发展&#xff0c;时代在进步&#xff0c;全球信息化、数字化的步伐越来越快&#xff0c;数字化转型是否成功也成为企业在未来发展中能否做大做强的关键因素。而数字化工厂就是制造业数字化发展的一个重要发展方向&#xff0c;那么究竟什么是数字化工厂呢&#xff1f;它和…

IDEA如何将本地项目推送到GitHub上?

大家好&#xff0c;我是G探险者。 IntelliJ IDEA 是一个强大的集成开发环境&#xff08;IDE&#xff09;&#xff0c;它支持多种编程语言和工具。它也内置了对Git和GitHub的支持&#xff0c;让开发者可以轻松地将本地项目推送到GitHub上。以下是一个操作手册&#xff0c;描述了…

JSP:Tag文件的使用

需求&#xff1a;多个JSP页面可能需要使用一些相同的信息 例如:导航栏、标题等。 目标&#xff1a;提高这些相同信息的代码的复用性。 方法&#xff1a;将这些相同的元素形成一种特殊的文件&#xff0c;以便所有页面都可以使用&#xff0c;即&#xff1a;Tag文件 1、Tag对…

关于lenra你需要了解的

monorepo&#xff1a;项目代码管理方式&#xff0c;单个仓库中管理多个项目是一种设计思想 lenra&#xff1a;是一种工具&#xff0c;对于使用npm和git管理多软件包代码仓库的工作流程进行优化 使用这些工具的优点&#xff1a; 公共依赖只要安装一次&#xff0c;Monorepo 中…

阿里云ECS服务器如何搭建并连接FTP,完整步骤

怎么用终端连接服务器就不多说了&#xff0c;直接开始搭建FTP。 我是用root账号执行的命令&#xff0c;如果不使用root账号&#xff0c;注意在命令前面加sudo。 一、安装FTP 我这里安装的是vsftpd。 1、检查是否已安装vsftpd&#xff1a; vsftpd -v如果出现了版本信息&…

JAVAEE---多线程

内核 内核时操作系统的核心 操作系统有内核态和用户态&#xff0c;像我们平时所用到的qq音乐&#xff0c;微信等都属于用户态执行的程序。那么qq音乐播放音乐需要用到扬声器&#xff0c;扬声器的操作就是在内核空间进行操作&#xff0c;用户态不能对其进行操作。 操作系统 …

QT专栏2 -Qt for Android

#2023年11月18日 # Qt version 6.6 JDK17 JDK 安装 Java Downloads | Oracle 设置环境变量 鼠标右键我的电脑&#xff0c;出现如下界面 Qt配置 改用JDK18&#xff0c;没有乱码&#xff0c;由于不影响APK产生。 做了好多尝试&#xff0c;更换JDK版本(11,18,19,21)&…

Python+OpenCV裂缝面积识别系统(部署教程&源码)

1.研究背景与意义 随着科技的不断发展&#xff0c;计算机视觉技术在各个领域中得到了广泛的应用。其中&#xff0c;图像处理是计算机视觉中的一个重要分支&#xff0c;它通过对图像进行数字化处理&#xff0c;提取出其中的有用信息&#xff0c;为后续的分析和应用提供支持。而…

Git——感谢尚硅谷官方文档

Git——尚硅谷学习笔记 第1章 Git 概述1.1 何为版本控制1.2 为什么需要版本控制1.3 版本控制工具1.4 Git 简史1.5 Git 工作机制1.6 Git 和代码托管中心 第2章 Git 安装第 3 章 Git 常用命令3.1 设置用户签名3.2 初始化本地库3.3 查看本地库状态3.4 添加暂存区3.4.1 将工作区的文…

大数据平台红蓝对抗 - 磨利刃,淬精兵! | 京东云技术团队

一、背景 目前大促备战常见备战工作&#xff1a;专项压测&#xff08;全链路压测、内部压测&#xff09;、灾备演练、降级演练、限流、巡检&#xff08;监控、应用健康度&#xff09;、混沌演练&#xff08;红蓝对抗&#xff09;&#xff0c;如下图所示。随着平台业务越来越复…

限时开发、码力全开、2w奖金!AGI Hackathon等你挑战!

AGI时代&#xff0c;我们已不再满足于简单的产品开发&#xff0c;与大模型结合的无限想象力&#xff0c;成为开发者们新的追求。 你有能力将想法转化为现实吗&#xff1f;你有勇气接受挑战&#xff0c;创造全新的AI应用吗&#xff1f; 如果你有热情&#xff0c;有信心&#xff…

深度学习之生成唐诗案例(Pytorch版)

主要思路&#xff1a; 对于唐诗生成来说&#xff0c;我们定义一个"S" 和 "E"作为开始和结束。 示例的唐诗大概有40000多首&#xff0c; 首先数据预处理&#xff0c;将唐诗加载到内存&#xff0c;生成对应的word2idx、idx2word、以及唐诗按顺序的字序列。…

注册中心CAP架构剖析

Nacos 支持 AP 或 CP AP Nacos 通过临时节点实现 AP 架构&#xff0c;将服务列表放在内存中&#xff1b; CP Nacos 通过持久化节点实现 CP 架构&#xff0c;将服务列表放在文件中&#xff0c;并同步到内存&#xff0c;通过 Raft 协议算法实现&#xff1b; 通过配置 epheme…

中科创达:所有产品都可以用生成式AI重做一遍

对于制造企业的数字化转型来说&#xff0c;生成式AI究竟具备怎样的意义和价值&#xff1f; 在与亚马逊云科技的合作中&#xff0c;中科创达对此有着深刻的领会和感悟。 生成式AI助力制造业数字化转型 “科技是第一生产力”&#xff0c;对于这句脍炙人口的名言&#xff0c;制造企…

x shell 用作串口调试助手

x shell 用作串口调试助手 Xshell 介绍 是一个强大的安全终端模拟软件&#xff0c;它支持SSH1, SSH2, 以及Microsoft Windows 平台的TELNET 协议。Xshell 通过互联网到远程主机的安全连接以及它创新性的设计和特色帮助用户在复杂的网络环境中享受他们的工作。 Xshell可以在Wi…

PDF文件无密码,如何解密?

PDF文件有两种密码&#xff0c;一个打开密码、一个限制编辑密码&#xff0c;因为PDF文件设置了密码&#xff0c;那么打开、编辑PDF文件就会受到限制。想要解密&#xff0c;我们需要输入正确的密码&#xff0c;但是有时候我们可能会出现忘记密码的情况&#xff0c;或者网上下载P…

tomcat (SCI)ServletContainerInitializer 的加载原理

问题&#xff1a;使用WebScoket的时候发现通过ServerEndpoint方式注册上去的url无法访问&#xff0c;报错404 经过排查发现在WsServerContainer这个类中的addEndpoint方法一直没有触发ServerEndpoint注解的扫描 通过该方法来源于StandardContext.startInternal()方法的调用如下…

基于单片机仓库温湿度监测报警系统仿真设计

**单片机设计介绍&#xff0c;基于单片机仓库温湿度监测报警系统仿真设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的仓库温湿度监测报警系统可以被设计成能够实时监测仓库内的温度和湿度&#xff0c;并根据预设…