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

六、引用

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. 引用比指针使用起来相对更安全。

总结:

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

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

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

相关文章

用vscode运行Java程序初体验

最近开始学习Java编程了&#xff0c;以前学习过C、C 、Python&#xff0c;主要用微软的visual studio code来运行python程序&#xff0c;于是就尝试了用vscode来运行java代码&#xff0c;记录一下使用的经验&#xff0c;帮助大家少走弯路。 安装了Java的集成编辑器IDE "Ec…

Docker环境安装

Docker环境安装Docker简介Docker工作原理Docker的应用场景Docker 的优点CentOS Docker 安装与配置Docker 安装Docker 配置Docker容器概念Docker容器操作拉取镜像删除镜像容器相关命令创建并启动容器停止和恢复容器删除容器Docker简介 Docker 是一个开源的应用容器引擎&#xf…

不卷不成魔,新时代的IT人员更需要卷,不卷不成活

简介 从2022年开始至今&#xff0c;IT界发生了很多巨大的变革带来了许多巨大的变化。 这些变革、这些变化导致了有人欢喜有人悲、有人迷茫有人焦虑。1年半来&#xff0c;迷茫、焦虑、精神内耗了也都差不多了&#xff0c;大家都已经认识到了现实&#xff0c;作为凡人的我们所能…

【防止恶意用户注册】-- 手机在网状态 API 的防欺诈应用解析

简介 手机在网状态 API 支持传入手机号码&#xff0c;查询手机号在网状态&#xff0c;返回在网、在网不可用、不在网&#xff08;销号/未启用/停机&#xff09;等多种状态&#xff0c;查询手机号在网状态之后&#xff0c;可以根据具体的业务需求来进行不同的处理。 本文主要介…

由libunifex来看Executor的任务构建

前言 之前的一篇文章讲述了future的优缺点&#xff0c;以及future的组合性&#xff0c;其中也讲述了构建任务DAG一些问题&#xff0c;同时给出了比较好的方案则是Executor。 Executor还未进入标准&#xff08;C23&#xff09;&#xff0c;Executor拥有惰性构建及良好的抽象模型…

c/c++:windows平台下依赖的动态库,c底层是汇编语言,程序断点调试,反汇编,vs快捷键

c/c&#xff1a;windows平台下依赖的动态库&#xff0c;c底层是汇编语言&#xff0c;程序断点调试&#xff0c;反汇编&#xff0c;vs快捷键 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;此时学会c的话&#xff0c; 我所知…

20230412-使用STM32实现内部flash模拟U盘

最近用STM32F103CBT6搞了个U盘功能 ​ 工程师干了几年后&#xff0c;基本会有小外包的生活&#xff0c;算是赚外快吧&#xff0c;搞小钱改善伙食&#xff0c;嘻嘻。。。。 ​ 最近有个客户找到我&#xff0c;说是否通过ST的单片机搞个U盘功能&#xff0c;有些文件通过U盘拖拽…

005:Mapbox GL添加全屏显示功能

第005个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+mapbox中添加全屏显示功能 。 直接复制下面的 vue+mapbox源代码,操作2分钟即可运行实现效果 文章目录 示例效果配置方式示例源代码(共60行)相关API参考:专栏目标示例效果 配置方式 1)查看基础设置:https://…

1~3年的测试工程师薪资陷入了瓶颈期,如何突破自己实现涨薪?

对于技术人员而言&#xff0c;职业规划一般分为两个方向&#xff1a;做技术、做管理。进入软件测试行业的新人都会从最基础的执行开始&#xff0c;然后是基本的功能测试。 随后大家会根据个人职业发展来进一步细化&#xff0c;有的走管理路线&#xff0c;成为主管、经理、项目…

Python 小型项目大全 76~81

七十六、井字棋 原文&#xff1a;http://inventwithpython.com/bigbookpython/project76.html 井字棋是一种在3 3网格上玩的经典纸笔游戏。玩家轮流放置 X 或 O 标记&#xff0c;试图连续获得三个。大多数井字棋都以平局告终&#xff0c;但如果你的对手不小心&#xff0c;你也…

AI 时代,提示词便是生产力

作者&#xff1a;明明如月学长&#xff0c; CSDN 博客专家&#xff0c;蚂蚁集团高级 Java 工程师&#xff0c;《性能优化方法论》作者、《解锁大厂思维&#xff1a;剖析《阿里巴巴Java开发手册》》、《再学经典&#xff1a;《EffectiveJava》独家解析》专栏作者。 热门文章推荐…

瑞芯微RK3568核心板强在何处?

RK3568核心板产品简介 RK3568核心板是武汉万象奥科基于瑞芯微Rockchip的RK3568设计的一款高性能核心板。该处理器集成了最新的高性能CPU、GPU&#xff0c;并拥有丰富的接口&#xff0c;非常适用于工业自动化控制、人机界面、中小型医疗分析器、电力等多种行业应用。 HD-RK3568-…

项目8:用户注册和登录的前后端联调

项目8&#xff1a;用户注册和登录的前后端联调 1.前端项目使用 2.前端项目注册模块 3.后端完成项目注册 4.前端项目登录模块 5.后端完成项目登录 6.用户认证&#xff08;校验用户是否登录&#xff09; 项目8&#xff1a;用户注册和登录的前后端联调 1.前端项目使用 直接…

【iOS】iOS语音通话回音消除(AEC)技术实现

一、前言 在语音通话、互动直播、语音转文字类应用或者游戏中&#xff0c;需要采集用户的麦克风音频数据&#xff0c;然后将音频数据发送给其它终端或者语音识别服务。如果直接使用采集的麦克风数据&#xff0c;就会存在回音问题。所谓回音就是在语音通话过程中&#xff0c;如…

九龙证券|这一刻,资本市场进入全新时代!

2023年4月10日&#xff0c;第一批10家主板注册制企业上市鸣锣敲钟&#xff0c;奏响了触及本钱商场灵魂深处革新的序曲。 动能切换中的我国对于高效资源配置的渴望&#xff0c;与革新进行时的本钱商场对于全面注册制的探究&#xff0c;一起凝集成一股连绵有力之暖流&#xff0c;…

2023年最强手机远程控制横测:ToDesk、向日葵、Airdroid三款APP免Root版本

前言 随着远程办公和远程协作的日益普及&#xff0c;跨设备、系统互通的远程控制软件已经成为职场人士不可或缺的工具之一。在国内&#xff0c;向日葵和ToDesk是最著名的远程控制软件&#xff1b;而在国外&#xff0c;则有微软远程桌面、AirDroid、TeamViewer、AnyDesk、Parse…

【华为机试真题详解JAVA实现】—Sudoku

目录 一、题目描述 二、解题代码 一、题目描述 问题描述:数独(Sudoku)是一款大众喜爱的数字逻辑游戏。玩家需要根据9X9盘面上的已知数字,推算出所有剩余空格的数字,并且满足每一行、每一列、每一个3X3粗线宫内的数字均含1-9,并且不重复。 例如: 输入 输出

Faster-RCNN代码解读2:快速上手使用

Faster-RCNN代码解读2&#xff1a;快速上手使用 前言 ​ 因为最近打算尝试一下Faster-RCNN的复现&#xff0c;不要多想&#xff0c;我还没有厉害到可以一个人复现所有代码。所以&#xff0c;是参考别人的代码&#xff0c;进行自己的解读。 ​ 代码来自于B站的UP主&#xff08;…

汽车电子相关术语介绍

一、相关术语介绍 1、汽车OTA 全称“Over-The-Air technology ”&#xff0c;即空中下载技术&#xff0c;通过移动通信的接口实现对软件进行远程管理&#xff0c;传统的做法到4S店通过整车OBD对相应的ECU进行软件升级。OTA技术最早2000年出现在日本&#xff0c;目前通过OTA方式…

FusionCharts Suite XT v3.20.0 Crack

FusionCharts Suite XT v3.20.0 改进了仪表的径向条形图和调整大小功能。2023 年 4 月 11 日 - 9:37新版本特征 添加了一个新方法“_changeXAxisCoordinates”&#xff0c;它允许用户将 x 轴更改为在图例或数据交互时自动居中对齐。更新了 Angular 集成以支持 Angular 版本 14 …