c++|引用

目录

一、引用概念

二、引用特性 

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

 四、使用场景


一、引用概念

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

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

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

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java动态代理JKD版本

1、ISale.java package com.atguigu; public interface ISale {void saleShaoBing();void saleJianBing();void saleYueBing();void saleManTou(); }2、WuDa.java package com.atguigu;//Target:目标类、目标对象 public class WuDa implements ISale{//target method:目标方法…

Polygon Miden VM架构总览

1. 计算类型 Programs程序有2种类型&#xff1a; 1&#xff09;Circuit电路&#xff1a;即&#xff0c;程序即电路。将程序转换为电路。2&#xff09;Virtual machine虚拟机&#xff1a;即&#xff0c;程序为电路的输入。【Miden VM属于此类型】 2. 何为ZK virtual machine…

用Markdown Nice写作

网址&#xff1a;https://www.mdnice.com/ 代码 表格 第二行用来对齐&#xff1a; -表示左对齐 :-:表示居中 -:表示右对齐 数学 上下标 分数 累加 幂 对数 根式 微积分 交集、并集 格式 标题 缩进 删除线 斜体 加粗 参考文献

【ArcGIS Pro二次开发】(77):ArcGIS Pro中图层的获取与解析

一、最简单的获取图层方式 通常情况下&#xff0c;如果要获取当前地图中的图层&#xff0c;可以用2种方法获取。 以下图为例&#xff1a; 一种是【map.Layers】属性获取&#xff0c;结果如下&#xff1a; 可以看出&#xff0c;这里只获取到了第一层级的图层&#xff0c;图层组…

目标检测 详解SSD原理,数据处理与复现

原理详解 前言 今天我们要读的这篇VGGNet&#xff08;《Very Deep Convolutional Networks For Large-Scale Image Recognition》&#xff09;&#xff0c;就是在AlexNet基础上对深度对网络性能的影响做了进一步的探索。它是ImageNet 2014年亚军&#xff0c;相比于AlexNet&am…

inBuilder低代码平台新特性推荐-第九期

各位知乎的友友们&#xff0c;大家好~ 今天来给大家带来的是inBuilder低代码平台特性推荐系列第九期——子表弹出新增&#xff01; 01 概述 子表弹出新增&#xff0c;是低代码平台提供的一种前端输入组件&#xff0c;在子表字段较多的场景中&#xff0c;有时为了方便…

代码随想录刷题】Day16 二叉树03

文章目录 1.【104】二叉树的最大深度&#xff08;优先掌握递归&#xff09;1.1 前言1.2 题目描述1.3 递归法java代码实现1.4 迭代法java代码实现1.5 相关练习题【559】N叉树的最大深度 2.【111】二叉树的最小深度&#xff08;优先掌握递归&#xff09;2.1 题目描述2.2 递归法ja…

智能高效的转运机器人,为物流行业注入新动力

在当今社会&#xff0c;随着科技的不断发展&#xff0c;机器人已经逐渐融入到我们的生活中。其中&#xff0c;转运机器人作为物流行业的新秀&#xff0c;正以其高效、智能的特点&#xff0c;引起了广泛的关注。 转运机器人&#xff0c;是指能够自主进行物品搬运和运输的机器人…

说一下类的生命周期

&#x1f47d;System.out.println(“&#x1f44b;&#x1f3fc;嗨&#xff0c;大家好&#xff0c;我是代码不会敲的小符&#xff0c;双非大四&#xff0c;Java实习中…”); &#x1f4da;System.out.println(“&#x1f388;如果文章中有错误的地方&#xff0c;恳请大家指正&a…

开始通过 Amazon SageMaker JumpStart 在亚马逊云科技上使用生成式 AI

目前&#xff0c;生成式 AI 正受到公众的广泛关注&#xff0c;人们围绕着许多人工智能技术展开讨论。很多客户一直在询问有关亚马逊云科技生成式 AI 解决方案的更多信息&#xff0c;本文将为您进行解答。 这篇文章通过一个真实的客户使用案例概述了生成式 AI&#xff0c;提供了…

京东数据分析软件(京东平台数据分析):2023年Q3扫地机器人行业消费报告

随着90后、00后逐渐成为消费主力军&#xff0c;他们对生活品质更加关注、健康意识进一步增强&#xff0c;再加上“懒人经济”的盛行&#xff0c;人们对扫地机器人的使用率和关注热情也不断增长。 根据鲸参谋电商数据分析平台的相关数据显示&#xff0c;今年7月份-9月份&#xf…

Linux ps -ef|grep去除 grep --color=auto信息

linux 监控 进程判断是否启动可通过该指令实现 ps -ef|grep java指令结果为 # -v 参数有过滤作用 ps -ef|grep java |grep -v grep

CentOS 8最小安装,VM使用这个内存占用小很多

文章目录 一、安装包下载作者使用的安装包 二、安装过程截图三、最小化安装拥有的外部命令四、查看ip&#xff08;方便ssh连接&#xff09;五、yum源有问题参考文档 一、安装包下载 CentOS 网站&#xff1a; https://www.centos.org/CentOS 维基&#xff1a; https://wiki.cen…

HugeGraph安装与使用

1、HugeGraph-Server与HugeGraph-Hubble下载 HugeGraph官方地址&#xff1a;https://hugegraph.apache.org/ 环境为&#xff1a;linux 官网是有模块版本对应关系,尽量下载较新版本,hubble1.5.0之前是studio功能比较少。官网已经下架server,其他模块下载也比较慢。可以在网上找…

xss-labs靶场1-5关

文章目录 前言一、靶场需要知道的前置知识点1、什么是xss攻击&#xff1f;2、xss攻击分为几大类1、反射型xss2、存储型xss3、dom型xss 3、xss攻击形成的条件 二、xss-labs关卡1-51、关卡12、关卡23、关卡34、关卡45、关卡5 总结 前言 此文章只用于学习和反思巩固xss攻击知识&a…

4.Gin HTML 模板渲染

4.Gin HTML 模板渲染 Gin HTML 模板渲染 1. 全部模板放在一个目录里面的配置方法 创建用于渲染的模板html templates/index.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title> …

如何入驻抖音本地生活服务商,附上便捷流程!

抖音作为一款短视频社交媒体应用&#xff0c;已经成为全球范围内数以亿计的用户的首选。而在普及的同时&#xff0c;短视频领域也在不断拓展自身的业务领域&#xff0c;其中之一就是本地生活服务。继抖音本地生活服务之后支付宝、视频号也相继开展了本地生活服务&#xff0c;用…

如何在IAR软件中使用STLINK V2编译下载和调试stm8单片机

安装使用IAR后&#xff0c;如使用系统默认设置&#xff0c;往往很难正常实现用stlink v2来下载和调试stm8芯片&#xff0c;我的解决方法如下&#xff1a; 1、打开项目的options菜单&#xff1a; 2、在项目的选项菜单中选择ST-LINK作为调试工具&#xff1a; 3、选择额外的输出…

六、程序员指南:数据平面开发套件

PORT HOTPLUG FRAMEWORK 端口热插拔框架为DPDK应用程序提供在运行时附加和分离端口的能力。由于该框架依赖于PMD实现&#xff0c;PMD无法处理的端口超出了该框架的范围。此外&#xff0c;在从DPDK应用程序分离端口后&#xff0c;该框架不提供从系统中移除设备的方法。对于由物…

1、数仓模型概述

1、问&#xff1a;什么是数据模型&#xff1f; 数仓领域中的模型指的是数据模型&#xff0c;要和商业分析中的模型不同 数据模型就是数据组织和存储方法&#xff0c;它强调从业务、数据存取和使用的角度合理的存储数据 2、问&#xff1a;模型和表的区别&#xff1f; 表是数据物…