【C++基础】引用(引用的概念;引用的特性;常引用;使用场景:做输出型参数、大对象传参、做输出型返回值、返回大对象的引用);引用和指针的区别)

news2024/11/25 12:19:21

六、引用

6.1 引用的概念

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

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

void TestRef()
{
    int a = 10;
    int& ra = a;//<====定义引用类型
    printf("%p\n", &a);
    printf("%p\n", &ra);
}

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


6.2 引用的特性

  1. 引用在定义时必须初始化
  2. 一个变量可以有多个引用
  3. 引用一旦引用一个实体,再不能引用其他实体

在这里插入图片描述

说明:

  • int& d = c; //c是a变量的别名,再为c取别名实际上还是引用a。相当于:int& d = a;
  • b = x; //b是a的别名,这句相当于:a = x; 将x赋值给b(a);

6.3 常引用

常引用即引用的类型用const进行修饰,表示不能通过该引用修改引用对象的值。类似于常量指针:const int *p;

  • 权限不能放大
  • 权限可以缩小
  • 显示或隐式类型转换都会产生同类型的临时变量,产生的临时变量具有常性。
void TestConstRef()
{
   //权限不能放大
   const int a = 20;
   //int& ra = a;
   const int& ra = a;
   
   //权限可以缩小
   int b = 30;
   const int& rb = b;
   
   //类型转换产生临时变量,产生的临时变量具有常性
   int i = 1;
   double d = i; //类型转换的过程中会产生同类型的临时变量
   //double& rd = i; //产生的临时变量具有常性   
   const double &rd = i; //此处临时变量的类型是const double
}

在这里插入图片描述

说明:

  • 权限放大或缩小的问题只存在于引用或指针上。普通变量不存在,因为普通变量不对源值做修改,只做值拷贝。
  • 如果使用引用传参,函数内如果不改变源值(输入型参数),那么建议尽量用const引用传参,提高程序的健壮性,且这样的形参具有非常强的接收度(类型转换)。

6.4 使用场景

1. 做输出型参数

引用做输出型参数,相比指针传参,使用更加方便。

void Swap(int& left, int& right){
   int temp = left;
   left = right;
   right = temp;
}
int main(){
	int a = 0, b = 2;
	Swap(a,b);
	return 0;
}

2. 大对象传参

以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效率是非常低下的,尤其是当参数或者返回值类型非常大时,效率就更低。

  • 我们可以做以下测试:
#include <time.h>
struct A{ int a[10000]; };
void TestFunc1(A a){}
void TestFunc2(A& a){}
void TestRefAndValue()
{
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(A)-time:" << end1 - begin1 << endl;
cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}
  • 测试结果:
    在这里插入图片描述
  • 得出结论:大对象使用引用传参,提高效率,节省空间。

3. 做输出型返回值

做输出型返回值,调用者可以修改返回对象。

int& Count()
{
   static int n = 0;
   n++;
   // ...
   return n;
}

同指针一样,不能返回局部变量的引用。但可以返回静态区或堆空间变量的引用。

4.(误区警示)返回局部变量的引用

下面代码输出什么结果?为什么?

int& Add(int a, int b)
{
    int c = a + b;
    return c;
}
int main()
{
    int& ret = Add(1, 2);
    Add(3, 4);
    cout << "Add(1, 2) is :"<< ret <<endl;
    return 0;
}
  • 测试结果:
    在这里插入图片描述
  • 解释原因:
    在这里插入图片描述
  • 得出结论:函数返回后,函数内的局部变量(栈帧空间)就销毁了。那么一定不能返回局部变量的引用,只能传值返回。

5. 返回大对象的引用

返回大对象的引用可以减少拷贝,提高效率。

  • 我们可以做以下测试:
#include <time.h>
struct A{ int a[10000]; };
A a;
// 值返回
A TestFunc1() { return a;}
// 引用返回
A& TestFunc2(){ return a;}
void TestReturnByRefOrValue()
{
// 以值作为函数的返回值类型
size_t begin1 = clock();
for (size_t i = 0; i < 100000; ++i)
TestFunc1();
size_t end1 = clock();
// 以引用作为函数的返回值类型
size_t begin2 = clock();
for (size_t i = 0; i < 100000; ++i)
TestFunc2();
size_t end2 = clock();
// 计算两个函数运算完成之后的时间
cout << "TestFunc1 time:" << end1 - begin1 << endl;
cout << "TestFunc2 time:" << end2 - begin2 << endl;
}
  • 测试结果:
    在这里插入图片描述
  • 得出结论:结合大对象的传参和返回测试,我们可以看出传值和引用在作为传参以及返回值类型上效率相差很大。

6.5 引用和指针的区别

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

#include <iostream>    
using namespace std;    
int main()    
{    
	int a = 10;    
	int& ra = a;    
	int* pa = &a;    
	cout<<"a = "<<a<<endl;    
	cout<<"ra = "<<ra<<endl;    
	cout<<"pa = "<<pa<<endl;    
	cout<<"*pa = "<<*pa<<endl<<endl;                                                                                                                  

	cout<<"&a = "<<&a<<endl;    
	cout<<"&ra = "<<&ra<<endl;    
	cout<<"&pa = "<<&pa<<endl;    
	cout<<"pa = "<<pa<<endl;    
return 0;    
}  

测试结果:
在这里插入图片描述

在底层实现上实际是有空间的,因为引用是按照指针方式来实现的。

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个字节)。
  6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小。
  7. 有多级指针,但是没有多级引用。
  8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理。
  9. 引用比指针使用起来相对更安全。

总结:

  • 指针更强大,更危险,更复杂;
  • 引用相对局限一些,更安全,更简单;

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

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

相关文章

矮床酸阻滞技术及其在酸回收行业的应用

废酸是如何产生的&#xff1f; 为什么要进行废酸回收&#xff1f; 在轧钢厂在轧制过程中&#xff0c;铁与氧或铁与燃料燃烧时的生成物(CO2、H2O等)的化学作用&#xff0c;而形成一层氧化层&#xff0c;称为氧化皮。其实我们日常生活中接触到的金属件&#xff0c;尤其是铁件上的…

mmsegmentation 训练自己的数据集

一. MMSegmentation是什么&#xff1f; MMSegmentation 是一个基于 PyTorch 的语义分割开源工具箱&#xff0c;它是 OpenMMLab 项目的一部分。他与MMDetection类似&#xff0c;集成了各种语义分割算法&#xff0c;可以快速验证语义分割效果。 二. 环境准备 参考&#xff1a…

【C++】模版(一)

泛型编程、模版&#xff08;一&#xff09;&#xff1a; 1.泛型编程&#xff1a; void Swap(int& left, int& right) {int temp left;left right;right temp; } void Swap(double& left, double& right) {double temp left;left right;right temp; }【…

电脑软件:office文档密码了怎么办,看完你就能自己找回了

Word、Excel、PPT作为大家日常办公的使用最频繁的办公软件。对于一些涉及个人隐私或者涉及企业机密性的文档文件&#xff0c;我们经常会给自己的文档加上密码。 但有时候文档时间比较久了、或者密码太多了&#xff0c;有时候我们会把文档的密码给忘记了&#xff0c;如果密码忘…

Shell练习

一、题目 1、编写函数&#xff0c;实现打印绿色OK和红色FAILED 判断是否有参数&#xff0c;存在为Ok&#xff0c;不存在为FAILED 2、编写函数&#xff0c;实现判断是否无位置参数&#xff0c;如无参数&#xff0c;提示错误 3、编写函数实现两个数字做为参数&#xff0c;返回最…

【大数据之Hadoop】十一、MapReduce之Shuffle、MapTask、ReduceTask工作机制

1 Shuffle机制 对于排序而言分为两个阶段&#xff0c;MapTask后和ReduceTask前。 2 MapTask工作机制 MapTask并行度由切片个数决定&#xff1b;切片个数由切片大小&#xff08;切片大小取决于块大小、maxsize&#xff08;Long的最大值&#xff09;和minsize&#xff08;默认为…

【华为机试真题详解JAVA实现】—字符串通配符

目录 一、题目描述 二、解题代码 一、题目描述 问题描述:在计算机中,通配符一种特殊语法,广泛应用于文件搜索、数据库、正则表达式等领域。现要求各位实现字符串通配符的算法。 要求: 实现如下2个通配符: *:匹配0个或以上的字符(注:能被*和?匹配的字符仅由英文字母和…

为一副通用纸牌设计数据结构

为一副通用纸牌设计数据结构 大家好&#xff0c;我是易安&#xff0c;今天我们来聊一道笔试题&#xff0c;这也是我曾经面试华为时做过的题&#xff0c;今天分享给大家。 题目&#xff1a; 如何设计一个通用的扑克牌数据结构&#xff1f;请解释如何继承它来实现特定的扑克游戏…

wps 不显示公式_当Excel中使用函数不显示结果只显示公式时,该怎么解决呢?

在使用Excel过程中&#xff0c;有时候Excel中使用函数后&#xff0c;不显示结果只显示公式。 如下面使用vlookup函数时&#xff0c;只显示公式不显示结果。 遇到这种情况我们该怎么处理呢&#xff1f; 接下来&#xff0c;介绍几种造成该情况的原因以及相对应的解决方法。 1.如果…

BarTender.Application COM 多次调用引发后台进程数不断增加的问题

引用文章方法测试&#xff0c;目前未发现问题. Closing Instance Of Bartender – BarTender Support Portal (seagullscientific.com) Closing Instance Of Bartender FollowFollowed by one person Legacy Poster 9 years ago Hello again, Ive got BarTender automating…

LLM:Vicuna 7B模型简单部署体验

0、引入1、保存权重文件到阿里云盘2、部署环境3、上传权重文件到30904、下载安装源码4.1 下载编译安装源码4.2 安装5、开始使用6、直接使用我的镜像立即开启人机对话Debug&#xff1a;可能的报错0、引入 随着ChatGPT的火热&#xff0c;科技公司们各显神通&#xff0c;针对大语…

设计模式之中介者模式(C++)

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 一、中介者模式是什么&#xff1f; 中介者模式是一种行为型的软件设计模式&#xff0c;也称为仲裁者模式&#xff0c;顾名思义&am…

nginx--基本配置

目录 1.安装目录 2.文件详解 2.编译参数 3.Nginx基本配置语法 1./etc/nginx/nginx.conf 2./etc/nginx/conf.d/default.conf 3.启动重启命令 4.设置404跳转页面 1./etc/nginx/conf.d/default.conf修改 ​2. 重启 5.最前面内容模块 6.事件模块 1.安装目录 # etc cd …

排序-时间复杂度

技巧&#xff1a;先处理 内层 一次排序&#xff0c;在处理外面 直接插入排序 升序 最坏(遇到降序)&#xff1a;O(N^2) 等差数列 123…(n-1) (n^2-n)/2 最好(有序) O(N) 希尔排序 gap 任何数字/2都是1 gap/3 1 保证gap最后是1 gap是多少 就分了多少组,每组数据可能少一点&…

根文件系统移植:bulidroot根文件系统搭建详细步骤

bulidroot根文件系统 文章目录bulidroot根文件系统1.buildroot 下载2 配置 buildroot1、配置 Target options2、配置 Toolchain3、 配置 System configuration4、配置 Filesystem images5、 禁止编译 Linux 内核和 uboot6、配置 Target packages3 编译 buildroot4 buildroot 根…

第一章 计算机网络概述

计算机网络基本概念 计算机网络是计算机技术与通信技术相互融合的产物计算机网络是互连的、自治的计算机的集合 自治——互连的计算机系统彼此独立&#xff0c;不存在主从或者控制与被控制关系互连——利用通信链路连接相互独立的计算机系统 目前最大的、应用最广泛的计算机网…

函数栈帧的创建和销毁(带你进一步深入理解函数的执行流程)

本文主题 什么是函数栈帧&#xff1f; 理解函数栈帧能解决什么问题&#xff1f; 函数栈帧的创建和销毁解析 1. 什么是函数栈帧 &#xff1f; 我们在写C语言代码的时候&#xff0c;经常会把一个独立的功能抽象为函数&#xff0c;所以C程序是以函数为基本单位的。 那函数是如何…

人人都是数据分析师-数据分析之数据图表可视化(上)

BI报表、运营同学的汇报报告中数据图表大多为 表格、折线图、柱状图和饼图&#xff0c;但是实际上还有很多具有代表性的可视化图表&#xff0c;因此将对常见的可视化图表进行介绍&#xff0c;希望这些图表可视化方法能够更好的提供数据的可用性。 导语 数据是我们在数据分析工…

通过1个IP地址同时采集2台西门子S7200 Smart的数据

一、应用场景 制药厂的颗粒包装机&#xff0c;控制系统由2台西门子S7200 Smart的PLC和1台昆仑通泰MCGS的触摸屏组成。现在MES管理系统&#xff0c;需要采集设备的数据&#xff0c;只提供一个IP地址&#xff0c;且IP地址和原系统不在同一个网络段内。 二、原系统架构 2台西门子S…

浅析DNS Rebinding

0x01 攻击简介 DNS Rebinding也叫做DNS重绑定攻击或者DNS重定向攻击。在这种攻击中&#xff0c;恶意网页会导致访问者运行客户端脚本&#xff0c;攻击网络上其他地方的计算机。 在介绍DNS Rebinding攻击机制之前我们先了解一下Web同源策略&#xff0c; Web同源策略 同源策略…