C++初阶引用

目录

    • 引用
      • 引用的特性
      • 使用输出型参数
      • 作返回值
      • 小总结
      • 引用的权限
      • 引用和指针

引用

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

比如周树人,在外的笔名叫鲁迅

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

int main()
{int a = 0;int& b = a; // 引用cout << &a << endl;cout << &b << endl;b++;a++;return 0;
}

在这里插入图片描述

引用的特性

  1. 引用在定义时必须初始化
  2. 一个变量可以有多个引用
  3. 引用一旦引用一个实体,再不能引用其他实体
int a = 0;
int& b = a
int x = 1;
// 赋值
b = x;

在这里插入图片描述
b还是和a的地址是一样的

使用输出型参数

二叉树的前序遍历

本题用c语言写的话:

void _preorderTraversal(struct TreeNode* root,int* a,int* pi)
{if(root==NULL)return NULL;a[(*pi)++]=root->val;_preorderTraversal(root->left,a,pi);_preorderTraversal(root->right,a,pi);
}

而c++用了引用后:

void _preorderTraversal(struct TreeNode* root, int* a, int& ri)
{if (root == NULL)return;printf("[%d] %d ", ri, root->val);a[ri] = root->val;++ri;_preorderTraversal(root->left, a, ri);_preorderTraversal(root->right, a, ri);
}//只放部分代码展示
int i = 0;
_preorderTraversal(root, a, i);

为什么不是直接传值(如图):
在这里插入图片描述
使用交换函数的使用也会方便许多

void swap(int& x1, int& x2)
{int tmp = x1;x1 = x2;x2 = tmp;
}
int main()
{int x = 0, y = 1;swap(x, y);return 0;
}

以前学习单链表尾插的时候

c语言二级指针的玩法

void PushBack(ListNode** pphead, int x)
{ListNode* newnode;if (*pphead == NULL){*pphead = newnode;}else{}
}int main()
{ListNode* plist = NULL;PushBack(&plist, 1);PushBack(&plist, 2);PushBack(&plist, 3);return 0;
}

C++,引用的玩法

typedef struct ListNode {int val;struct ListNode* next;
}ListNode, *PListNode;void PushBack(ListNode*& phead, int x)
//void PushBack(PListNode& phead, int x) 和上面代码一样的
{ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));// ...if (phead == NULL){phead = newnode;}else{}
}int main()
{ListNode* plist = NULL;PushBack(plist, 1);PushBack(plist, 2);PushBack(plist, 3);return 0;
}

plistnode是对struct Listnode*的typedef,也就是指针的typedef。plistnode代表结构体指针

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

作返回值

在这里插入图片描述

不是把n返回给ret,因为函数调用结束的时候,n已经销毁了,所以不敢拿n去返回。所以设定是会生成临时变量,可能会用寄存器也可能是其他方式。在返回前拷贝给临时变量。把n的值拷贝到寄存器,然后寄存器充当返回值,一般值小的时候。当n比较大的时候,会在栈帧中间部分提前压一块空间作返回值。

还有一种返回方式叫传引用返回
返回的是n的引用,也就是返回的是n
可能会出现问题,n销毁了还返回n的别名,类似于野指针
在这里插入图片描述

int& Count()
{int n = 0;n++;// ...return n;
}int main()
{int ret = Count();cout << ret << endl;cout << ret << endl;return 0;
}

打印的结果可能是1,也可能是随机值,取决于这个栈帧销毁后空间会不会被置成随机值,得看环境,在vs下的结果是1 1

来看下面的代码会造成什么不一样的情况:

int& Count()
{int n = 0;n++;// ...return n;
}int main()
{int& ret = Count();cout << ret << endl;cout << ret << endl;return 0;
}

在这里插入图片描述

cout<<ret是一个函数调用,流插入这个函数调用还是在count空间上,只是栈帧大小可能比count大或小,函数调用时定义一些变量的时候就会对比如原来n的位置进行覆盖。

第一次调用没有覆盖因为调用函数先传参。传参过去之后函数建立栈帧但是传的值不会受到影响。第二次调用的时候想去取值,就会发现值被覆盖了。

不一定会覆盖,如果变量定义在太前一般都会被覆盖。比如在n前面定义一个大的数组就可能不被覆盖了。那么两次打印结果就都为1了(vs下)。

int& Count()
{int a[1000];int n = 0;n++;return n;
}

再看一个情况

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

可能是随机值可能是7,看栈帧销毁后空间会不会被置成随机值。

传引用返回让代码优化的例子:

C的接口设计
读取第i个位置的值
int SLAT(struct SeqList* ps, int i)
{assert(i < ps->size);// ...return ps->a[i];
}
修改第i个位置的值
void SLModify(struct SeqList* ps, int i, int x)
{assert(i < ps->size);// ...ps->a[i] = x;
}CPP接口设计
读 or 修改第i个位置的值
int& SLAT(struct SeqList& ps, int i)
{assert(i < ps.size);// ...return (ps.a[i]);
}int main()
{struct SeqList s;s.size = 3;// ...SLAT(s, 0) = 10;SLAT(s, 1) = 20;SLAT(s, 2) = 30;cout << SLAT(s, 0) << endl;cout << SLAT(s, 1) << endl;cout << SLAT(s, 2) << endl;return 0;
}

小总结

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

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

传引用传参(任何时候都可以用)
1、提高效率
2、输出型参数(形参的修改,影响的实参)

传引用返回(出了函数作用域对象还在才可以用)
1、提高效率
2、修改返回对象

引用的权限

在引用的过程中
权限可以平移
权限可以缩小
权限不能放大

int func()
{int a = 0;return a;
}int main()
{const int& ret = func();const int a = 0;// 权限的放大// int& b = a;//int b = a; 可以的,因为这里是赋值拷贝,b修改不影响a// 权限的平移const int& c = a;// 权限的缩小int x = 0;const int& y = x;//x更改y也会改,y不能更改int i = 0;const double& d = i;return 0;
}

在这里插入图片描述

在c/c++中,double d = i;发生类型转换(提升)、截断等时会产生一个临时变量,不是把 i 直接给d,是给一个double类型的临时变量,
double& d = i;是不行的原因是临时变量具有常性,这是一种权限的放大,用const引用临时变量就可以了。

在这里插入图片描述
返回的不是a,是a的拷贝,右边会报错是因为临时变量具有常性
用const引用临时变量就可以了const int& ret = func();
不用担心临时变量销毁,用const引用后会延长对象的生命周期,相当于ret出了作用域临时变量才销毁

引用和指针

语法上理解引用不开空间
下层到底是怎么样的?

在这里插入图片描述

lea是取地址,对a取地址放到寄存器,再把寄存器放到p1
从图可以看到引用和指针在底层是一样的,引用也是存地址

再看看使用的时候:
在这里插入图片描述
所以底层只有指针,没有引用
像正版和盗版,内核是一样的,但是可以通过品牌区分出来

引用和指针的不同点:

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

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

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

相关文章

探索创意之路:稳定扩散AI绘画指南

文章目录 引言第一部分&#xff1a;了解稳定扩散AI绘画1.1 稳定扩散AI绘画简介1.2 稳定扩散AI绘画的优势 第二部分&#xff1a;使用稳定扩散AI绘画2.1 获取稳定扩散AI绘画工具2.2 准备绘画素材和设置参数2.3 进行AI绘画 第三部分&#xff1a;发挥创意&#xff0c;创作精彩绘画3…

阿里云AK创建

要在阿里云上创建 Access Key&#xff08;AK&#xff09;&#xff0c;您需要按照以下步骤进行操作&#xff1a; 登录到阿里云控制台&#xff08;[https://www.aliyun.com/?utm_contentse_1014243503)&#xff09;。 点击右上方的主账号&#xff0c;点击“AccessKey管理”。 …

P1064 [NOIP2006 提高组] 金明的预算方案 (依赖背包问题)(内附封面)

[NOIP2006 提高组] 金明的预算方案 题目描述 金明今天很开心&#xff0c;家里购置的新房就要领钥匙了&#xff0c;新房里有一间金明自己专用的很宽敞的房间。更让他高兴的是&#xff0c;妈妈昨天对他说&#xff1a;“你的房间需要购买哪些物品&#xff0c;怎么布置&#xff0…

R语言【Tidyverse、Tidymodel】的机器学习方法

机器学习已经成为继理论、实验和数值计算之后的科研“第四范式”&#xff0c;是发现新规律&#xff0c;总结和分析实验结果的利器。机器学习涉及的理论和方法繁多&#xff0c;编程相当复杂&#xff0c;一直是阻碍机器学习大范围应用的主要困难之一&#xff0c;由此诞生了Python…

python人工智能可以干什么,python人工智能能干什么

大家好&#xff0c;给大家分享一下python做人工智能需要什么水平&#xff0c;很多人还不知道这一点。下面详细解释一下。现在让我们来看看&#xff01; 人工智能包含常用机器学习和深度学习两个很重要的模块&#xff0c;而python拥有matplotlib、Numpy、sklearn、keras等大量的…

【深度学习笔记】深度学习框架

本专栏是网易云课堂人工智能课程《神经网络与深度学习》的学习笔记&#xff0c;视频由网易云课堂与 deeplearning.ai 联合出品&#xff0c;主讲人是吴恩达 Andrew Ng 教授。感兴趣的网友可以观看网易云课堂的视频进行深入学习&#xff0c;视频的链接如下&#xff1a; 神经网络和…

snap xxx has “install-snap“ change in progress

error description * 系重复安装&#xff0c;进程冲突 solution 展示snap的改变 然后sudo snap abort 22即可终止该进程 之后重新运行install command&#xff5e;&#xff5e; PS: ubuntu有时候加载不出来&#xff0c;执行resolvectl flush-caches&#xff0c;清除dns缓存…

【计算机视觉 | 图像分割】arxiv 计算机视觉关于图像分割的学术速递(8 月 1 日论文合集)

文章目录 一、分割|语义相关(16篇)1.1 DPMix: Mixture of Depth and Point Cloud Video Experts for 4D Action Segmentation1.2 Investigating and Improving Latent Density Segmentation Models for Aleatoric Uncertainty Quantification in Medical Imaging1.3 Domain Ada…

网络音频终端音频编码解码终端

网络对讲终端SV-7011V 网络对讲终端SV-7011V&#xff0c;采用了ARM音频DSP架构&#xff0c;集网络对讲、网络广播、监听等功能于一身&#xff0c;内置麦克风、配置line out、line in、Mic in功能输出接口&#xff0c;适用于学校&#xff0c;机场&#xff0c;广场等场所。 产品…

纯css实现九宫格图片

本篇文章所分享的内容主要涉及到结构伪类选择器&#xff0c;不熟悉的小伙伴可以了解一下&#xff0c;在常用的css选择器中我也有分享相关内容。 话不多说&#xff0c;接下来我们直接上代码&#xff1a; <!DOCTYPE html> <html lang"en"><head>&l…

51单片机(普中HC6800-EM3 V3.0)实验例程软件分析 实验一 点亮第一个LED

目录 前言 一、原理图及知识点介绍 1.1、LED原理图 1.2、MCU51原理图 二、代码分析 知识点一&#xff1a;#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器 知识点二&#xff1a;你知道sfr P0 0x80;是怎么来的呢为什么要赋值0x80&#xff…

Stable Diffusion AI绘画学习指南【本地环境搭建win+mac】

一、硬件配配置要求 系统&#xff1a;windows 10 / Mac os 硬盘&#xff1a;C 盘预留 15GB 以上&#xff0c;其他盘 50GB 以上,Stable Ddiffusion的很多大模型都是以 GB 起步。 显卡&#xff1a;4GB 以上&#xff0c;建议 8GB, 效率高&#xff0c;能玩大尺寸的图 CPU&…

SpringMVC框架——First Day

目录 三层架构 MVC模型 SpringMVC 快速入门案例 SpringMVC的概述&#xff08;了解&#xff09; SpringMVC在三层架构的位置 SpringMVC的优势&#xff08;了解&#xff09; 创建SpringMVC的Maven项目 1.在pom.xml中添加所需要的jar包 2.在工程的web.xml中配置核心Spring…

Linux修改系统语言

sudo dpkg-reconfigure locales 按pagedown键&#xff0c;移动红色光标到 zh_CN.UTF-8 UTF-8&#xff0c;空格标记*号&#xff08;没标记下一页没有这一项&#xff09;&#xff0c;回车。 下一页选择 zh_CN.UTF-8。 如果找不到 dpkg-reconfigure whereis dpkg-reconfigure …

Java的变量与常量

目录 变量 声明变量 变量的声明类型 变量的声明方式&#xff1a;变量名 变量名的标识符 初始化变量 常量 关键字final 类常量 总结 变量和常量都是用来存储值和数据的基本数据类型存储方式&#xff0c;但二者之间有一些关键差别。 变量 在Java中&#xff0c;每个变…

深入理解TCP三次握手:连接可靠性与安全风险

目录 导言TCP简介和工作原理的回顾TCP三次握手的目的和步骤TCP三次握手过程中可能出现的问题和安全风险为什么TCP三次握手是必要的&#xff1f;是否可以增加或减少三次握手的次数&#xff1f;TCP四次挥手与三次握手的异同点 导言 在网络通信中&#xff0c;TCP&#xff08;Tra…

sqoop

一、bg 可以在关系型数据库和hdfs、hive、hbase之间导数 导入&#xff1a;从RDBMS到hdfs、hive、hbase 导出&#xff1a;相反 sqoop1 和sqoop2 (1.99.x)不兼容&#xff0c;sqoop2 并没有生产的稳定版本&#xff0c; Sqoop1 import原理(导入) 从传统数据库获取元数据信息&…

8.5day06 框架基础--反射+注解

文章目录 反射获取类的各种信息获取类的字节码文件 注解元注解 复习redis两道算法题 摆烂了&#xff0c;不想学啦&#xff01;&#xff01;&#xff01; 反射 反射主要用来做框架; 学习内容 获取类的各种信息 第一步 加载类&#xff0c;获取类的字节码文件 第二步 获取类的…

Matlab的信号频谱分析——FFT变换

Matlab的信号频谱分析——FFT变换 Matlab的信号频谱分析 FFT是离散傅立叶变换的快速算法&#xff0c;可以将一个时域信号变换到频域。 有些信号在时域上是很难看出什么特征的。但是如果变换到频域之后&#xff0c;就很容易看出特征了。 这就是很多信号分析采用FFT变换的原因…

巨人网络宣布与华为达成鸿蒙生态合作,2024年发布原始征途手游

巨人网络宣布与华为达成鸿蒙生态合作&#xff0c;官方公众号发布的消息确认。 巨人网络与华为宣布战略合作&#xff0c;旨在实现技术互补、成果共享和商业共赢。 巨人网络将利用基于HarmonyOS的核心特性&#xff0c;如“可分可合、自由流转、一次开发多端部署”&#xff0c;创…