一篇文章带你搞懂C++引用(建议收藏)

引用

6.1 引用概念

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

比如:李逵,在家称为"铁牛",江湖上人称"黑旋风"

typedef 是给类型取别名

引用是给变量取别名

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

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

int main()
{
    int a = 0;//定义一个变量a,赋值为0,
    int &b = a;//这里的&不是取地址符,而是引用,给变量取别名, &定义在一个类型和变量之间为引用
    //定义一个空间为a,又给这个空间a取别名为b
    
    int a = 0;
    int b = a;//这种写法和上面是完全不同的,
    //这里是 定义一个变量为a,a的值是0,定义一个变量为b,用a的0初始化
    return 0;
}

如何证明是否公用一块空间?


#include <iostream>
using namespace std;

int main()
{
    int a = 0;
    int& b = a;

    cout << &b << endl;//取地址
    cout << &a << endl;//取地址
    
    a++;
    b++;

    return 0;
}

6.2 引用的特性

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

int main()
{
    int a = 1;
    int& b;//引用在定义时必须初始化
    return 0;
}

2. 一个变量可以有多个引用(可以取无限个别名)

int main()
{
    int a = 1;
    int &b = a;
    int &c = a;
    int &d = c;//还可以给别名取别名
    
    a++;
    
    return 0;
}

3. 引用一旦引用一个实体,再不能引用其他实体

指针指向一个实体后,可以改变,引用不可以改变

int main()
{
    int a = 1;
    int& b = a;
    int& c = a;
    int& d = c;
    int x = 10;
    //引用一旦拥有一个实体,再不能引用其他实体
    b = x;b是x的别名呢?还是x赋值为b?

    return 0;
}

这里把x赋值给b

6.3 常引用

权限不能放大

int main()
{
    int a = 13;
    int& b = a;
    
    //权限不能放大
    const int c = 20;
    //int& d = c;
    const int& d = c;
    
    //权限可以缩小,从只读的角度
    int e = 25;
    const int& f = e;      
    return 0;
}

隐式类型转换和强制类型转换都是会产生临时变量

所有的转换都不是对原变量进行转换和提升,而是之间产生临时变量

并不会改变原变量类型,

这里本质上是权限的放大和缩小

类型转换中间是会产生临时变量的,临时变量具有常性,相当于const修饰

int main()
{
    int ii = 1;
    doubble dd = ii;//int类型可以给double,会发生隐式类型转换
    double& rdd = ii;//double类型的数据可以变成int的引用吗?
    //不可以  这里不能引用的原因不是说类型不匹配,而是不能进行权限的放大
    const double& rdd = ii;
    //这里可以引用是因为权限的平移 中间产生的临时变量也是相当于const修饰的变量
    //此时rdd引用的不是ii,而是int转换成double之间产生的临时变量,
    //这个临时变量具有常性

    return 0;
}

const 可以把一个常量起一个别名

const int& x = 10;

应用场景:

如果使用引用传参,函数内如果不改变n,那么尽量用const引用传参

const引用传参会有非常强的接受度

        

6.4 引用的使用场景

1.做参数

a.输出型参数

b.大对象传参,提高效率

2.做返回值

a.输出型返回对象,调用者可以修改返回对象.

b.减少拷贝提高效率,

不是什么时候都可以使用传引用返回,要注意使用场景

出了作用域,返回对象还在可以用传引用返回

出了作用域,返回对象销毁.那么一定不能用引用返回,一定要用传值返回

1. 做参数 --(引用的输出型参数)

如果不用引用,传入的是实参的一份临时拷贝,形参的改变不会影响实参

如果想改变 必须使用指针 传入地址

在C++里,这里x是a的别名,y是b的别名,x和y的交换就是a和b的交换

void Swap(int& x, int& y)
{
    int tmp = x;
    x = y;
    y = tmp;
}
int main()
{
    int a = 3;
    int b = 5;
    cout <<"交换前:" << a << ' ' << b << endl;
    Swap(a, b);
    cout <<"交换后:" << a << ' ' << b << endl;
    return 0;
}

例如:有些书里会把数据结构里链表部分弄得很复杂

typedef struct SListNode
{
    .....
}SLTNode,*PSLTNode;
//这里的*PSLTNode 相当于typedef struct SListNode* PSLTNode  给这个结构体指针起了个别名 PSLTNode
void SListPushBack(PSLTNode& phead,int x)//这里的引用相当于 phead是list的别名,phead的改变会影响list
{
    .....
}
int main()
{
    SLTNode* list = NULL;
    SListPushBack(list,1);
    SListPushBack(list,1);
    SListPushBack(list,1);
    return 0;
}

大对象传参 提高效率

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

2. 做返回值

传值返回 都会生成一个返回对象拷贝作为函数调用的返回值

返回的是n吗 是用n的拷贝来做表达式的返回值

为什么编译器要这么做?

如果这里没有static ,n返回时,出了该作用域 会销毁 置为随机值

传引用返回:

引用返回的语法含义:返回返回对象的别名

不是什么时候都可以使用传引用返回,要注意使用场景

返回n的别名 越界了为什么不报错呢?

ret的结果是未定义的 如果栈帧结束时,系统清理栈帧置成随机值,那么这里ret的结果就是随机值

下面程序使用引用返回本质是不对的,结果是没有保障的

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

    return 0;
}

如果引用ret ,那么相当于 ret也是n的别名,结果会不会有所不同

int& Count()
{
    int n = 0;
        n++;
    // ...
    return n;
}
int main()
{
    int& ret = Count();
    printf("%d\n", ret);
    printf("%d\n", ret);
    printf("%d\n", ret);
    printf("%d\n", ret);
    return 0;
}

总结:出了函数作用域,返回对象就销毁了,那么一定不能用引用返回,一定要用传值返回

什么时候可以用引用返回?

使用static

1. static修饰变量

   a. 函数中局部变量:

      声明周期延长:该变量不随函数结束而结束

      初始化:只在第一次调用该函数时进行初始化

      记忆性:后序调用时,该变量使用前一次函数调用完成之后保存的值

      存储位置:不会存储在栈上,放在数据段

int& Count()
{
    static int n = 0;
        n++;
    // ...
    return n;
}
int main()
{
    int& ret = Count();
    printf("%d\n", ret);
    printf("%d\n", ret);
    printf("%d\n", ret);
    printf("%d\n", ret);
    return 0;
}
const引用

为什么这里给c取别名不可以?

本质上是权相的放大 c自己是const类型 不可以修改

用d取别名 使得d是权限的放大 可以修改

#include <iostream>

using namespace std;

int main()
{

    int a = 10;//权限的的平移
    int& b = a;

    const int c = 20;//权限的放大
    int& d = c;
 
          int e = 30;//权限的缩小
          const int&f = e;//只读的角度变成你的别名

    return 0;
}

权限不可以放大,但是权限可以缩小

int main()
{
    int ii = 1;
    double dd = ii;//发生隐式类型转换
    double& rdd = ii;//double不可以变成int的引用
    //不可以使用的本质原因是 因为权限的放大,因为 int 类型要转换成double类型
    //中间要先生成一个double类型的临时变量,相当于 const double ii;
    //double& dd = const double ii; //权限的放大不可以
    const double& rdd = ii;
    //可以使用权限的平移
    return 0;
}

强制转换并不是改变原变量类型 他也是产生一个临时变量

如果使用引用传参,函数内如果不改变n,那么尽量用const引用传参

void func1(int& n)
{
    
}
void func2(const int& n)
{
    
}
int main()
{
    int a = 10;
    const int b =20;
    func1(a);
    func1(b);
    func1(30);
    
    func2(a);
    func2(b);
    func2(30);
    double c = 1.11;
    
    func2(c);
    return 0;
}

指针和引用的差别

指针和引用用途基本是相似的

但有

1.使用场景的差别

引用和指针的不同点:

1. 引用在定义时必须初始化,指针没有要求

2. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型

实体

3. 没有NULL引用,但有NULL指针

4. 在sizeof中含义不同引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占

4个字节)

5. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小

6. 有多级指针,但是没有多级引用

7. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理

8. 引用比指针使用起来相对更安全

2.语法特性以及底层原理的差别

从语法角度而言,引用没有开辟空间,指针开了四个字节或者8个字节的空间

底层实现角度,那么引用底层是用指针实现的

个人水平不足 如果代码中有错误,可以多多在评论区指出,一定会及时修改!
谢谢大家看到这里 觉得有收获的话可以三连一下 一起加油!

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

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

相关文章

查询SQL02:寻找用户推荐人

问题描述 找出那些 没有被 id 2 的客户 推荐 的客户的姓名。 以 任意顺序 返回结果表。 结果格式如下所示。 题目分析&#xff1a; 这题主要是要看这null值会不会用&#xff0c;如果说Java玩多了&#xff0c;你去写SQL时就会有问题。在SQL中判断是不是null值用的是is null或…

一文读懂AI时代GPU的内存新宠-HBM

一文读懂GPU最强辅助&#xff1a;HBM HBM&#xff0c;即高带宽内存&#xff0c;是一项领先的3D堆叠DRAM技术&#xff0c;专为高性能计算和图形处理单元&#xff08;GPU&#xff09;设计&#xff0c;满足其对内存带宽和容量的极致需求。该技术由AMD与海力士携手研发&#xff0c;…

聊聊二叉堆、红黑树、时间轮在定时任务中的应用

定时任务作为常用的一种调度方式&#xff0c;在各大系统得到了广泛的应用。 笔者也曾写过两篇关于定时任务框架介绍的文章&#xff1a; 《介绍一下,spring cloud下的另一种定时任务解决方案》《四叉堆在GO中的应用-定时任务timer》 之前都是以如何使用为主&#xff0c;这次从…

【UML用户指南】-10-对高级结构建模-高级类

目录 1、类目 2、高级类 3、可见性 4、实例范围和静态范围 5、抽象元素、叶子元素和多态性元素 6、多重性 7、属性 8、操作 9、模板类 10、标准元素 1、类目 类目 &#xff08;classifier&#xff09;是描述结构特征和行为特征的机制。类目包括类、关联、接口、数据类…

【YOLOv10改进[CONV]】使用DualConv二次创新C2f模块实现轻量化 + 含全部代码和详细修改方式 + 手撕结构图 + 全网首发

本文将使用DualConv二次创新C2f模块实现轻量化,助力YOLOv10目标检测效果的实践,文中含全部代码、详细修改方式以及手撕结构图。助您轻松理解改进的方法。 改进前和改进后的参数对比: 目录 一 DualConv 1 结合33卷积和11卷积核 2 DualConv 3 可视化 二 C2f_DualConv助…

Python 知识图谱补全,Knowledge Graph Completion,基于大模型的知识图谱补全,基于LLMs的KGC任务

今天讲一篇文章《Exploring Large Language Models for Knowledge Graph Completion》 &#xff0c;这篇文章主题&#xff1a;基于大模型做知识图谱补全 1.文章主要思想&#xff1a; 本章描述知识图谱补全中的三个任务&#xff1a;三元组分类、关系预测和实体(链接)预测&…

2024Dragon Knight CTF复现web

穿梭隐藏的密钥 知识点&#xff1a;fuzz技术、ssrf、本地绕过、data伪协议、md4弱比较、数组绕过 首先看看页面的源代码&#xff0c;但是发现f12和鼠标右键都被禁用了 用ctrlu查看&#xff0c;发现一个可疑页面 访问看看&#xff0c;发现还是只有一张图&#xff0c;查看源代…

短视频的景别:成都鼎茂宏升文化传媒公司

短视频的景别&#xff1a;探索视觉艺术的魅力 在短视频的浩瀚海洋中&#xff0c;每一个画面都承载着独特的情感和信息。这些画面&#xff0c;通过不同的景别展现&#xff0c;构成了短视频的叙事基础和视觉美感。成都鼎茂宏升文化传媒公司旨在探讨短视频中景别的运用&#xff0…

揭露:抖音外卖区域代理骗局真相,绝不可错过!

自2023年11月23日抖音发布清退服务商的公告后&#xff0c;由官方认证的抖音外卖平台全国代理正式成为历史&#xff0c;而后&#xff0c;抖音外卖平台区域代理接棒&#xff0c;帮助抖音开拓本地生活市场。在此背景下&#xff0c;抖音外卖平台区域代理的申请人数与日俱增&#xf…

AIConnect赋能加持丨AI+DEPIN 共同推动AI发展的技术与运用峰会圆满落幕

6月6日&#xff0c;由AIConnect主办&#xff0c;JuCoin协办的「AIDePIN 共同推动AI发展的技术与应用」峰会在胡志明市圆满落幕&#xff01;此次活动不仅是AIConnect生态在市场推广和技术应用方面的重要一步&#xff0c;也标志着JuCoin在推动AI与DePIN技术融合中的又一里程碑。 …

算法金 | 这次终于能把张量(Tensor)搞清楚了!

大侠幸会&#xff0c;在下全网同名[算法金] 0 基础转 AI 上岸&#xff0c;多个算法赛 Top [日更万日&#xff0c;让更多人享受智能乐趣] 1. 张量&#xff08;Tensor&#xff09;基础概念 1.1 张量的定义与重要性 张量是深度学习中用于表示数据的核心结构&#xff0c;它可以视…

K8s存储对象的使用

背景和概念 容器中的文件在磁盘上是临时存放的&#xff0c;这给在容器中运行较重要的应用带来一些问题&#xff1a; 当容器崩溃或停止时&#xff0c;此时容器状态未保存&#xff0c; 因此在容器生命周期内创建或修改的所有文件都将丢失。另外 在崩溃期间&#xff0c;kubelet 会…

javaWeb4 Maven

Maven-管理和构建java项目的工具 基于POM的概念 1.依赖管理&#xff1a;管理项目依赖的jar包 &#xff0c;避免版本冲突 2.统一项目结构&#xff1a;比如统一eclipse IDEA等开发工具 3.项目构建&#xff1a;标准跨平台的自动化项目构建方式。有标准构建流程&#xff0c;能快速…

VS2019创建c++动态链接库dll与调用方法

VS2019创建c动态链接库dll与调用方法 1.点击文件-》新建-》项目&#xff0c;输入dll,选择具有导出项的(DLL)动态链接库 2.输入一个文件名&#xff1a;dll2 头文件.h 3.添加加减法函数&#xff1a; // 下列 ifdef 块是创建使从 DLL 导出更简单的 // 宏的标准方法。此 DLL 中的…

【高频】什么是索引的下推和覆盖

面试回答&#xff1a; 索引的下推是指数据库引擎在执行查询时&#xff0c;将过滤条件尽可能地应用到索引上&#xff0c;以减少需要检索的数据量&#xff0c;从而提高查询性能。这样可以减少数据库引擎从磁盘加载的数据量&#xff0c;提高查询效率。覆盖索引是指一个索引包含了…

C++青少年简明教程:C++函数

C青少年简明教程&#xff1a;C函数 C函数是一段可重复使用的代码&#xff0c;用于执行特定的任务&#xff0c;可以提高代码的可读性和可维护性。函数可以接受参数&#xff08;输入&#xff09;并返回一个值&#xff08;输出&#xff09;&#xff0c;也可以没有参数和返回值。 …

范闲获取到庆帝与神庙的往来信件,用AES进行破解

关注微信公众号 数据分析螺丝钉 免费领取价值万元的python/java/商业分析/数据结构与算法学习资料 在《庆余年2》中&#xff0c;范闲与庆帝和神庙之间的权谋斗争愈演愈烈。一次偶然的机会&#xff0c;范闲从庆帝的密室中获取到几封与神庙往来的密信。然而&#xff0c;这封信件…

算法-分治策略

概念 分治算法&#xff08;Divide and Conquer&#xff09;是一种解决问题的策略&#xff0c;它将一个问题分解成若干个规模较小的相同问题&#xff0c;然后递归地解决这些子问题&#xff0c;最后合并子问题的解得到原问题的解。分治算法的基本思想是将复杂问题分解成若干个较…

电源变压器的作用和性能

电源变压器的主要作用是改变输入电压的大小&#xff0c;通常用于降低电压或升高电压&#xff0c;以便适应不同设备的需求。它们还可以提供隔离&#xff0c;使得输出电路与输入电路之间电气隔离&#xff0c;从而提高安全性。性能方面&#xff0c;电源变压器需要具有高效率、低温…

二叉树-堆的详解

一&#xff0c;树的概念 1&#xff0c;树的概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。 把它叫做树是因为它看起来像一棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;而叶朝下的。 有…