初谈C++:引用

在这里插入图片描述

文章目录

  • 前言
  • 概述
  • 引用特性
  • 应用场景
    • 做参数
    • 做返回值
  • 传值、传引用效率比较
  • 引用和指针的区别

前言

在学习C语言的时候会遇到指针,会有一级指针、二级指针…很容易让人头昏脑胀。在C++里面,引入了引用的概念,会减少对指针的使用。引用相当于给一个变量起了一个别名,比如“高总”指的是小编。

概述

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

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

#include<iostream>using namespace std;int main()
{int a = 10;int& b = a;int& c = a;return 0;
}

调试过程中,发现引用变量的地址和引用实体的地址是相同的,也就是说引用实际上就是给一个变量起了一个别名。

在这里插入图片描述
注意引用类型必须和引用实体是同种类型的

引用和C语言中只针的操作其实差不多,我们在反汇编语言中可以看到:

在这里插入图片描述

引用特性

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

int main()
{int a = 10;int& ra;   //错误语法return 0;
}

在这段代码中,int& ra没有初始化,编译器会报错

在这里插入图片描述
正确代码:

int main()
{int a = 10;int& ra = a;return 0;
}
  1. 一个变量可以有多个引用
int main()
{int a = 10;int& ra = a;int& rra = a;return 0;
}

上述代码中,rarra都是对变量a的引用。这是没有问题的,比如小编有不止一个外号。

  1. 引用一旦引用一个实体,再不能引用其他实体
int main()
{int a = 10;int temp = 20;int& ra = a;ra = temp;cout << ra << endl;cout << a << endl;return 0;
}

运行结果:

在这里插入图片描述

在这段代码中ra是变量a的引用,ra=temp是将temp的值赋给ra引用的实体,即a

应用场景

做参数

效果:

  1. 做输出型参数,形参的改变可以影响实参
  2. 减少拷贝,提高效率
void Swap(int& a,int& b)
{int tmp = a;a = b;b = tmp;
}int main()
{int x = 0, y = 1;Swap(x, y);cout << x << " " << y << endl;return 0;
}

运行结果:

在这里插入图片描述

上述代码确实实现了交换两个数字的功能

形参a是对实参x的引用,和x表示同一块空间;形参b是对实参y的引用,和y表示的是用一块空间。所以,在函数内交换ab实际上就是在交换xy

做返回值

以前的传值返回:

int Add(int a, int b)
{int sum = a + b;return sum;
}int main()
{int x = 4;int y = 3;int ans = Add(x, y);cout << ans << endl;return 0;
}

这里随着函数栈帧调用的结束,sum也会销毁。那为什么最后还能打印出最终结果?

对于这种传值返回,会有一个临时变量的生成,这种临时变量是用来存储返回值的,当返回值比较小的时候,这个临时变量就是寄存器。通过反汇编,我们可以看到:把sum值赋给了寄存器eax

在这里插入图片描述
以上是在局部变量中

那么以satic修饰的变量在静态区,此变量虽然不会随着调用函数的栈帧销毁而销毁,但是在传值返回的时候也会创建临时变量。

在这里插入图片描述
因此不难看出,传值返回都会生成一个中间变量。


以上是以前的写法,那么在学了引用后,我们需要使用引用返回:

引用返回和传值返回不同,函数栈帧销毁后,不需要创建临时变量来存储返回值。但是函数栈帧销毁后,返回的变量仍然存在。

也就是说,返回的变量不能存储在调用的函数的栈帧中,所以返回的变量是存储在静态区的变量或者是在堆上申请的变量。

先来看下面的代码:

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;
}

运行结果:

在这里插入图片描述
并不是我们需要的结果,这是为什么呢?

主函数中,首先调用的是函数Add(1,2),此函数函数调用结束后,该函数对应的栈空间就被回收了,也就是说Add函数中c变量就没有意义了。中ret引用Add函数返回值实际应用的就是一块已经被释放的空间。
然后调用Add(3,4)函数,此函数函数调用结束后,该函数对应的栈空间就被回收了,也就是说Add函数中c变量就没有意义了。注意:空间被收回是说空间不能使用了,但是空间本身还在,而ret引用的c的位置被修改成了7,因此ret的值就被修改了。


关于引用返回需要强调的是:

  1. 函数运行时,系统需要给该函数开辟独立的栈空间,用来保存该函数的形参、局部变量以及一些寄存信息等
  2. 函数运行结束后,该函数的栈空间就会被系统收回
  3. 空间被收回指的是这块栈空间暂时不能被使用,但是内存还在

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

传值、传引用效率比较

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

#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;
}int main()
{TestRefAndValue();return 0;
}

运行结果:

在这里插入图片描述
性能比较:

#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;
}int main()
{TestReturnByRefOrValue();return 0;
}

运行结果:

在这里插入图片描述

通过上述代码的比较,发现传值和指针在作为传参以及返回值类型上效率相差很大

引用和指针的区别

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

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

引用和指针的不同点:

  1. 引用概念上定义一个变量的别名,指针存储一个变量地址。
  2. 引用在定义时必须初始化,指针没有要求
  3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
  4. 没有NULL引用,但有NULL指针
  5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
  6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
  7. 有多级指针,但是没有多级引用
  8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
  9. 引用比指针使用起来相对更安全

在这里插入图片描述

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

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

相关文章

日历功能——C语言

实现日历功能&#xff0c;输入年份月份&#xff0c;输出日历 #include<stdio.h>int leap_year(int year) {if(year % 4 0 && year % 100 ! 0 || year % 400 0){return 1;}else{return 0;} }int determine_year_month_day(int *day,int month,int year) {if(mo…

ECharts 图表嵌入表格样式的demo

心累。。。 如果条件允许&#xff0c;还是强烈建议 用 Echartshtml 来实现&#xff08;表格部分由 html 来弄&#xff09;。 这里是调研阶段&#xff0c;想看看 ECharts 原生能做到什么程度。 先贴上样图&#xff1a; 贴上完整代码&#xff1a; <!DOCTYPE html> <…

SQL注入攻击 - 基于布尔的盲注

环境准备:构建完善的安全渗透测试环境:推荐工具、资源和下载链接_渗透测试靶机下载-CSDN博客 查看靶场详情:SQL Injections 一、判定是否有注入点 以下是一个常见的步骤: 在URL中尝试输入特殊字符,如: " \ -- 等,并观察页面返回的内容。在URL中尝试输入错误的…

MySQL:MVCC原理详解

MySQL是允许多用户同时操作数据库的&#xff0c;那么就会出现多个事务的并发场景。那么再并发场景会出现很多问题&#xff1a;脏读、不可重复读、幻读的问题。 而解决这些问题所用到的方法就是&#xff1a;MVCC 多版本并发控制。而这个MVCC的实现是基于read_view、undoLog 如…

【Linux驱动】块设备驱动(一)—— 注册块设备

针对块设备驱动将分为两部分介绍&#xff0c;第一部分是注册块设备&#xff0c;即将块设备成功添加到内核&#xff1b;第二部分是介绍如何读写块设备&#xff0c;因为没有实际块设备&#xff0c;这里选择使用内存来模拟块设备。 一、认识块设备 1、什么是块设备 块设备针对的…

浏览器内存泄漏排查指南

1、setTimeout执行原理 使用setInterval/setTimeOut遇到的坑 - 掘金 2、Chrome自带的Performance工具 当我们怀疑页面发生了内存泄漏的时候&#xff0c;可以先用Performance录制一段时间内页面的内存变化。 点击开始录制执行可能引起内存泄漏的操作点击停止录制 如果录制结束…

【Java基础】之进程与线程

进程与线程 1. 线程与进程1.1 概念1.2 区别与联系 2. 线程的5种状态和切换2.1 简单介绍2.2 状态切换2.2.1 重点情况 3. 线程中常见的方法4. 线程池 1. 线程与进程 1.1 概念 进程&#xff1a;资源分配的基本单元&#xff0c;如QQ音乐 线程&#xff1a;资源调度的基本单元&…

关于美图秀秀如何给证件照快速抠图换背景操作

日常生活中一些经常处理的小技巧&#xff0c;记录以备以后使用&#xff0c;也方便别人&#xff0c;希望能帮到大家。 1、先导入一张相片&#xff0c;点击AI人像抠图&#xff1b; 2、再点应用当前效果&#xff1b; 3、再点击自动抠图或手动抠图或形状抠图;就可以点击换背景 4、…

Pyecharts绘制多彩气泡图:从基础到高级定制【第49篇—python:多彩气泡图】

Pyecharts绘制多种炫酷气泡图参数说明代码实战 引言 数据可视化是数据分析中不可或缺的一环&#xff0c;而Pyecharts作为一款基于Echarts的Python图表库&#xff0c;提供了丰富的图表类型&#xff0c;其中气泡图是一种常用于展示三维数据的炫酷图表。本文将介绍如何使用Pyech…

Git 介绍 与 配置

Git 介绍 Git是一个分布式版本控制系统&#xff0c;用于跟踪文件的更改和协作开发。它可以管理项目的版本历史记录&#xff0c;并允许多个开发者在同一时间进行并行开发。 解决上图产生的问题就出现了git 分布式版本控制系统 看下图 Git 配置 Git的基本配置包括用户名和电子邮…

leetcode hot100岛屿的最大面积

本题是让求岛屿的最大面积&#xff0c;和上一个题求岛屿的数量类似&#xff0c;也是通过dfs或者bfs进行求解。 那么&#xff0c;首先我们判断dfs函数的参数&#xff0c;需要grid[][]&#xff0c;需要横坐标i&#xff0c;纵坐标j。那么&#xff0c;这里我们求的是最大面积&…

2万块的郎酒,都是我们惯的

文 | 琥珀酒研社 作者 | 五画 当我看到郎酒拿出快2万一瓶纪念酒的时候&#xff0c;我就知道&#xff0c;这场高价酒的喧嚣和吵闹&#xff0c;又到了一个新的高度。 和别的行业有所不同&#xff0c;白酒很少谈智商税&#xff0c;再高的价格&#xff0c;总有个冠冕堂皇的理由。…

Linux文本三剑客---awk经典案例

awk&#xff08;是一种处理文本文件的应用程序&#xff0c;它依次处理文件的每一行&#xff0c;并读取里面的每一个字段。&#xff09; awk 包含几个特殊的内建变量&#xff08;可直接用&#xff09;如下所示&#xff1a; 1、获取根分区剩余大小 #可以使用df -h命令来查看所有…

ElementUI Form:InputNumber 计数器

ElementUI安装与使用指南 InputNumber 计数器 点击下载learnelementuispringboot项目源码 效果图 el-radio.vue 页面效果图 项目里el-input-number.vue代码 <script> export default {name: el_input_number,data() {return {num: 1,num5: 1,num6: 1,num7: 1,num8:…

Logstash 7.7.1版本安装系统梳理

前言 上一篇文章介绍了 《ElasticSearch7.7.1集群搭建 & Kibana安装》&#xff0c;今天说一下 Logstash的安卓和配置&#xff1b; Logstash是一个开源的数据收集引擎&#xff0c;具有实时管道功能。它可以动态地将来自不同数据源的数据统一起来&#xff0c;并将数据标准化…

多线程代码案例之线程池

作者简介&#xff1a; zoro-1&#xff0c;目前大二&#xff0c;正在学习Java&#xff0c;数据结构&#xff0c;javaee等 作者主页&#xff1a; zoro-1的主页 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f496; 创建线程池 public class Poo…

Xmind文件转CSV、Excel文件

不知道小伙伴们有没有发现&#xff0c;新版Xmind文件转Excel是收费的功能。因为自己不经常使用Xmind&#xff0c;收到一个这样的文件&#xff0c;需要转换成Excel&#xff0c;也不不值得破解或者付费。在github上有一个工具XMind2TestCase&#xff0c;非常不错&#xff0c;可以…

网安人必看!CISP家族顶流证书攻略

网络安全已成为当今的热门领域&#xff0c;证书在职业发展中的重要性不言而喻。但是&#xff0c;证书市场五花八门&#xff0c;选择适合自己的证书可是个大问题。别担心&#xff0c;今天我们就来聊聊CISP家族的几个热门认证&#xff0c;让你在网络安全领域的发展更加顺利&#…

在虚拟环境中导出和安装requirements.txt文件

背景&#xff1a; ​ 一般在项目开放完成后&#xff0c;我们需要把项目工程所需要的虚拟环境依赖包导出&#xff0c;以便在服务器上进行安装和配置&#xff0c;这时候我们一般将所需要的python相关库导出一个txt文件&#xff0c;后续在服务器上之前pip安装即可。 措施&#x…

【HarmonyOS 4.0 应用开发实战】TypeScript入门之接口详讲

个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名大三在校生&#xff0c;喜欢AI编程&#x1f38b; &#x1f43b;‍❄️个人主页&#x1f947;&#xff1a;落798. &#x1f43c;个人WeChat&#xff1a;hmmwx53 &#x1f54a;️系列专栏&#xff1a;&#x1f5bc;️…