【浅尝C++】引用

在这里插入图片描述

🎈归属专栏:浅尝C++
🚗个人主页:Jammingpro
🐟记录一句:大半夜写博客的感觉就是不一样!!


文章前言:本篇文章简要介绍C++中的引用,每个介绍的技术点,在可能的情况下,都附上代码了。


文章目录

  • 引用的概念
  • 引用的特征
  • 常引用
  • 使用场景
    • 做参数
    • 做返回值
  • 值传递与引用传递效率比较
  • 引用和指针的区别


引用的概念

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间
就跟现实生活中一样,一个人原名叫做光头强,再给他取了个别名叫帅靓仔。一样地,我们为a变量申请一个空间地址为0x00001010,再为a变量取一个别名叫做superA。此时,a和superA指向的内存地址均是0x00001010。
在这里插入图片描述
引用的语法为:变量类型& 引用变量名(对象名) = 引用实体;
下面给出引用语法的示例代码,并演示引用实体及引用变量指向同一内存空间。

#include <iostream>
using namespace std;int main()
{int a = 100;int& superA = a;cout << "a's address is " << &a << endl;cout << "superA's address is " << &superA << endl;return 0;
}

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

引用的特征

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

对于上面3个属性,给出如下代码进行验证👇

#include <iostream>
using namespace std;int main()
{int age = 18;// int& refer_age1;	//该代码编译不通过,引用在定义时必须初始化int& refer_age2 = age;int& refer_age3 = age;//这里说明了,一个变量可以有多个引用//age变量有refer_age2和refer_age3两个引用。当然,还可以设置更多age的引用int newAge = 20;refer_age2 = newAge;//这行代码可以正常运行,但这里不是将refer_age2变为newAge的引用,而是将age的值赋值为newAge的值cout << "age's value is " << age << " and its address is " << &age << endl;cout << "refer_age2's value is " << refer_age2 << " and its address is " << &refer_age2 << endl;cout << "newAge's value is " << newAge << " and its address is " << &newAge << endl;return 0;
}

上面代码运行后,agerefer_age2的地址相同,agerefer_age2newAge的值均为20。refer_age2 = newAge并不是将refer_age2设置为newAge的引用,而是将refer_age2指向的引用实体的值设置为newAge的值。这行语句的效果等同于age = newAge。因此,引用一旦引用一个实体,再不能引用其他实体。

常引用

我们知道,常量的值是不可以修改的。因此,常量的引用也不可以被赋值,即不能被修改数值。

void TestConstRef()
{const int a = 10;//int& ra = a;  //该条语句编译时会出错,a为常量const int& ra = a;//int& b = 10;  //该语句编译时会出错,b为常量const int&b = 10;double d = 12.34;//int& rd = d;	//该语句编译时会出错,类型不同const int&rd = d;
}

上面代码中,a是常量,因而,其引用必须为const类型的引用。也就是说,引用的操作权限不能高于被引用实体的操作权限(即a不能被修改,则其引用也一定不能被修改)。同理,10是字面常量,因此,引用它的b也必须是常量。将int类型的引用指向double类型的引用实体,此时会发生类型转换,即double类型会被转换为int类型。转换时,编译器会尝试创建一个const int的临时对象,并将double类型转换为const int类型。因此,若给引用赋予引用实体时需要类型转换,该引用也必须为const类型。
在这里插入图片描述

使用场景

做参数

在没有引用之前,我们需要使用地址传递才能实现swap函数。如下面的代码👇

#include <iostream>
using namespace std;void swapByAddress(int* left, int* right)
{int temp = *left;*left = *right;*right = temp;
}int main()
{int num1 = 10, num2 = 20;cout << "num1 = " << num1 << ", num2 = " << num2 << endl;swapByAddress(&num1, &num2);cout << "num1 = " << num1 << ", num2 = " << num2 << endl;return 0;
}

有了引用之后,我们就不再需要使用取地址符来传递变量地址,并在给变量赋值时使用解引用运算符了。实现代码如下👇

#include <iostream>
using namespace std;void swapByReference(int& left, int& right)
{int temp = left;left = right;right = temp;
}int main()
{int num1 = 10, num2 = 20;cout << "num1 = " << num1 << ", num2 = " << num2 << endl;swapByReference(num1, num2);cout << "num1 = " << num1 << ", num2 = " << num2 << endl;return 0;
}

做返回值

与引用做参数一样,如果返回值为指针类型,后续需要对指针进行操作,整体操作比较繁琐。我们可以选择使用引用做返回值。代码如下👇

#include <iostream>int& addGongDe()
{static int gongde = 0;++gongde;return gongde;
}int main()
{int myGongDe = addGongde();cout << myGongde << endl;return 0;
}

与指针类型做返回值一样,以引用做返回值时,如果函数返回时,出了函数作用域,如果返回对象还在(还没还给系统),则可以使用引用返回,如果已经还给系统了,则必须使用传值返回。

下面给出一段运行时出错的代码👇

#include <iostream>
using namespace std;int& Add(int a, int b)
{int c = a + b;return c;
}
int main()
{int& ret = Add(1, 2);cout << "Add(1, 2) is : " << ret << endl;return 0;
}

main函数调用Add函数时,栈中增加了abc三个变量,在Add函数返回后,这三个变量的内存空间被释放。在输出时,ret访问了已经释放的内存空间,故出现错误。

值传递与引用传递效率比较

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

当传递的参数数据量较大时,可以发现:引用传递效率明显高于值传递。下面为该结论的验证代码👇

#include <iostream>
#include <time.h>
using namespace std;struct Ming{ int a[10000]; };void Func1(Ming a){}void Func2(Ming& a){}void TestRefAndValue()
{Ming a;//以值作为函数参数size_t begin1 = clock();for(size_t i = 0; i < 10000; ++i)Func1(a);size_t end1 = clock();//以引用作为函数参数size_t begin2 = clock();for(size_t i = 0; i < 10000; ++i)Func2(a);size_t end2 = clock();//分别计算两个函数运行结束后的时间cout << "Func1(a)->time:" << end1 - begin1 << endl;cout <<  "Func2(s)->time:" << end2 - begin2 << endl;
}int main()
{TestRefAndValue();return 0;
}

不仅在传递函数参数时可以使用引用传递,引用还可以作为返回值。一样地,在数据量较大时,引用做返回值的效率明显高于值传递做返回值。下面为验证该结论的代码👇

#include <iostream>
#include <time.h>
using namespace std;struct Ming{ int a[10000]; };Ming a;
//值返回
Ming Func1(){ return a; }
//引用返回
Ming& Func2(){ return a; }void TestReturnByRefOrValue()
{//以值作为函数的返回值类型size_t begin1 = clock();for(size_t i = 0; i < 100000; ++i)Func1();size_t end1 = clock();//以引用作为函数的返回值类型size_t begin2 = clock();for(size_t i = 0; i < 100000; ++i)Func2();size_t end2 = clock();//计算两个函数运算完成之后的时间cout << "Func1()-time : " << end1 - begin1 << endl;cout << "Func2()-time : " << end2 - begin2 << endl;
}int main()
{TestReturnByRefOrValue();return 0;
}

从上面的文字和代码中,我们可以总结出:在程序能正常运行的前提下,传递函数参数及函数返回值时,尽量使用引用传递。

引用和指针的区别

在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用一块空间。在底层实现上实际是有空间的,因为引用是按照指针方式来实现的。

int main()
{int a = 10;int& refer_a = a;refer_a = 20;int* point_a = &a;*point_a = 20;return 0;
}

引用和指针的不同点:

  1. 引用概念上定义一个变量的别名,指针存储一个变量地址。
  2. 引用在定义时必须初始化,指针没有要求
  3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何 一个同类型实体
  4. 没有NULL引用,但有NULL指针 【引用必须初始化】
  5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32 位平台下占4个字节)
void test()
{long long num = 16;long long& refer_num = num;long* point_num = &num;cout << sizeof(refer_num) << endl;	//等于sizeof(long long)cout << sizeof(point_num) << endl;	//输出结果为指针所占内存空间大小
}
  1. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
void test()
{int arr[10] = {0};for(size_t i = 0; i < 10; i++){arr[i] = i * 2;}int& refer_arr1 = &arr[1];int* point_arr1 = &arr[1];++refer_arr1;++point_arr1;cout << refer_arr1 << endl;		//输出结果为2cout << (*point_arr1) << endl;	//输出结果为4
}
  1. 有多级指针,但是没有多级引用
  2. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
void test()
{int num = 16;int& refer_num = num;int* point_num = &num;cout << refer_num << endl;		//不需要解引用cout << (*point_num) << endl;	//解引用操作
}
  1. 引用比指针使用起来相对更安全

文章结语:这篇文章对C++中的引用进行了简要的介绍。
🎈欢迎进入浅尝C++专栏,查看更多文章。
如果上述内容有任何问题,欢迎在下方留言区指正b( ̄▽ ̄)d

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

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

相关文章

Gitlab-ci:从零开始的前端自动化部署

一.概念介绍 1.1 gitlab-ci && 自动化部署工具的运行机制 以gitlab-ci为例&#xff1a; (1) 通过在项目根目录下配置.gitlab-ci.yml文件&#xff0c;可以控制ci流程的不同阶段&#xff0c;例如install/检查/编译/部署服务器。gitlab平台会扫描.gitlab-ci.yml文件&…

QML实现的图片浏览器

很久之前实现了一个QWidget版本的图片浏览器:基于Qt5的图片浏览器QHImageViewer 今天用QML也实现一个,功能差不多: ●悬浮工具栏 ●支持图片缩放、旋转、还原、旋转、拖动。 ●拖动图片时,释放鼠标图片会惯性滑动。 ●支持左右翻页查看文件夹中的图片。 ●支持保存图片至本…

【ONE·MySQL || 常见的基本函数】

总言 主要内容&#xff1a;介绍了MySQL中常用的基本函数。一些聚合函数、时间日期函数、字符串函数、数字函数等。       文章目录 总言1、聚合函数1.1、汇总1.2、COUNT()函数1.2.1、基本说明1.2.2、使用演示 1.3、SUM( )函数1.3.1、基本说明1.3.2、使用演示 1.4、AVG( )函…

java基础之Java8新特性-Optional

目录 1.简介 2.Optional类常用方法 3.示例代码 4.示例代码仓库地址 1.简介 Java 8引入了一个重要的新特性&#xff0c;即Optional类。Optional类是为了解决空指针异常而设计的。 在Java中&#xff0c;当我们尝试访问一个空对象的属性或调用其方法时&#xff0c;很容易抛出…

【服务器数据恢复】Hyper-V虚拟化数据恢复案例

服务器数据恢复环境&#xff1a; Windows Server操作系统服务器&#xff0c;部署Hyper-V虚拟化环境&#xff0c;虚拟机的硬盘文件和配置文件存放在某品牌MD3200存储中&#xff0c;MD3200存储中有一组由4块硬盘组成的raid5阵列&#xff0c;存放虚拟机的数据文件&#xff1b;另外…

国内首款支持苹果Find My芯片-伦茨科技ST17H6x

深圳市伦茨科技有限公司&#xff08;以下简称“伦茨科技”&#xff09;发布ST17H6x Soc平台。成为继Nordic之后全球第二家取得Apple Find My「查找」认证的芯片厂家&#xff0c;该平台提供可通过Apple Find My认证的Apple查找&#xff08;Find My&#xff09;功能集成解决方案。…

建立四叉树[中等]

一、题目 给你一个n * n矩阵grid&#xff0c;矩阵由若干0和1组成。请你用四叉树表示该矩阵grid。你需要返回能表示矩阵grid的四叉树的根结点。四叉树数据结构中&#xff0c;每个内部节点只有四个子节点。此外&#xff0c;每个节点都有两个属性&#xff1a; 【1】val&#xff1…

GLSL着色器入门(持续更新中...)

目录 第一章&#xff1a;OpenGL works with triangles 第二章&#xff1a; Parallel Processing 第章 推荐来自b站的课程004 GLSL is not Javascript_哔哩哔哩_bilibili 第一章&#xff1a;OpenGL works with triangles 当我们谈论GLSL着色器时&#xff0c;其实就是在说怎么…

C语言奇偶数交换排序问题

目录 问题描述如下&#xff1a; move函数的具体实现&#xff1a; main函数 运行效果 代码&#xff08;注意看注释&#xff09;&#xff1a; 问题描述如下&#xff1a; 给定一个整数数组&#xff0c;要求将其中的奇数元素全部移动到前面&#xff0c;偶数元素全…

【深度学习:Foundation Models】基础模型完整指南

【深度学习&#xff1a;Foundation Models】基础模型完整指南 什么是基础模型&#xff1f;基础模型背后的 5 项人工智能原理根据大量数据进行预训练自我监督学习过度拟合微调和快速工程&#xff08;适应性强&#xff09;广义的 基础模型的用例基础模型的类型计算机视觉基础模型…

最新版CleanMyMac X4.14.7智能清理mac磁盘垃圾工具

CleanMyMac X是一款专业的Mac清理软件&#xff0c;可智能清理mac磁盘垃圾和多余语言安装包&#xff0c;快速释放电脑内存&#xff0c;轻松管理和升级Mac上的应用。同时CleanMyMac X可以强力卸载恶意软件&#xff0c;修复系统漏洞&#xff0c;一键扫描和优化Mac系统&#xff0c;…

借助GPT理解 “ Android中 点击弹框外部 取消弹框”

在平常的开发工作中 或 阅读技术博客/书籍 时&#xff0c;难免会遇到我们不懂的知识点&#xff0c;网络上搜索的资料 需要有准确性&#xff0c;系统性&#xff0c;可实操性。 这样的资料查询很费时间且还不一定能找到&#xff0c;但是如果借助训练过的的gpt&#xff0c;就会省下…

[Kubernetes]7. K8s包管理工具Helm、使用Helm部署mongodb集群(主从数据库集群)

上一节讲解了[Kubernetes]6. k8s Pod配置管理ConfigMap & Secret以及传递环境变量的使用,k8s的命名空间以及使用kubens管理命名空间的使用,这里来介绍一下Helm的使用 一.Helm相关介绍 1.介绍 在 kubernetes 系统上部署容器化应用时需要事 先手动编写资源配置清单文件 以…

Prometheus实战篇:Prometheus监控docker

Prometheus实战篇:Prometheus监控docker 准备环境 监控docker 为了能够获取到Docker容器的运行状态,用户可以通过Docker的stats命令获取当前主机上运行容器的统计信息,可以查看容器的CPU利用率,内存使用量,网络IO总量以及磁盘IO总量等信息. docker stats除了使用命令以外,用户…

【占用网络】SurroundOcc:基于环视相机实现3D语义占用预测 ICCV 2023

前言 本文分享“占用网络”方案中&#xff0c;来自ICCV 2023的SurroundOcc&#xff0c;它基于环视相机实现3D语义占用预测。 使用空间交叉注意力将多相机图像信息提升到3D体素特征&#xff0c;即3D体素Query到2D图像中查询融合特征的思想。 然后使用3D卷积逐步对体素特征进行…

如何快速断行、分割行、切割行、换行、限制每行字数、平均分割每行字数、序号自动换行、关键字断行等等内容格式整理

首先&#xff0c;需要用到的这个工具&#xff1a; 百度 密码&#xff1a;qwu2蓝奏云 密码&#xff1a;2r1z 打开工具&#xff0c;切换到“文章工作域”&#xff08;嗯...默认就是&#xff09; 找到这个&#xff0c;多内容断行分割 点击打开&#xff0c;出现如下窗口设置 相关的…

Netty-Netty基础应用与了解

前言 Netty 的优势 1、 API 使用简单&#xff0c;开发门槛低&#xff1b; 2、功能强大&#xff0c;预置了多种编解码功能&#xff0c;支持多种主流协议&#xff1b; 3、定制能力强&#xff0c;可以通过 ChannelHandler 对通信框架进行灵活地扩展&#xff1b; 4、性能高…

【教程】通过Excel宏/Pandas两种方法来自动添加渐变数据条

这种数据真的很难看懂&#xff1a; 一般会对其画折线图或者数据条&#xff0c;相比起来就非常直观&#xff1a; 但是每一列都要手动这样设置就非常累了&#xff0c;所以这里就用到了VBA宏(或者Pandas)。 VBA宏方法 从这里进入宏&#xff1a; 随便写一个宏名后点创建&#xff1…

LangChain 69 向量数据库Pinecone入门

LangChain系列文章 LangChain 50 深入理解LangChain 表达式语言十三 自定义pipeline函数 LangChain Expression Language (LCEL)LangChain 51 深入理解LangChain 表达式语言十四 自动修复配置RunnableConfig LangChain Expression Language (LCEL)LangChain 52 深入理解LangCh…

机器视觉在OCR字符检测的应用

在产品质量 检测过程中&#xff0c;对于字符、条码等标识信息的识别、读取、检测是非常重要的一部分&#xff0c;比如在食品饮料包装检测中&#xff0c;生产日期 、保质期 、生产批号 、条码等字符信息是产品管理和追溯必不可缺的&#xff0c;因此利用机器视觉技术进行OCR字符采…