C++——引用

引用的概念

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

相当于是给被引用的变量取了一个小名,但是相当于是同一个变量。

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

void TestRef()

{

     int a = 10;

     int& ra = a;// 定义引用类型

    printf("%p\n",&a);

    printf("%p\n",&ra);

}

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

对引用变量的修改就是对引用实体的修改

这里可以对输出型参数进行应用

1.交换函数

void Swap(int& x, int& y)

{

    int tmp = x;

    x = y;

    y = tmp;

}

int main()

{

    int a = 10, b = 20;

    Swap(a,b);

    cout << "a = " << a << endl <<  "b = " << b;

}

2.单链表

本来,对于单链表的pushback而言,我们要传入二级指针

typedef struct SListNode

{

    SLDataType data;

    struct SListNode* next;

}SLnode;

void SLPushBack(SLNode** pphead, SLDateType x)

{

       SLNode** pphead, SLDateType x;

       if (*pphead == NULL)

       {

            *pphead = newnode;

       }

       else

       {

            SLNode* tail = *pphead;

            while(tail -> next != NULL)

            {

                   tail = tail -> next;

            }

            tail -> next = newnode;

       }

}

有了引用,我们就可以这样写

typedef struct SListNode

{

    SLDataType data;

    struct SListNode* next;

}SLnode;

void SLPushBack(SLNode*& phead, SLDateType x)

{

       if (phead == NULL)

       {

            phead = newnode;

       }

       else

       {

            SLNode* tail = phead;

            while(tail -> next != NULL)

            {

                   tail = tail -> next;

            }

            tail -> next = newnode;

       }

}

 

引用特性

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

void TestRef()

{

  int a = 10;

 // int&ra ;//该条语句编译时会出错

  int& ra = a;

  int&  rra = a;

}

2.一个变量可以有多个引用

还可以给别名再取别名,这样,所有的名字其实都指的是同一个变量。

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

常引用

void TestConstRef()

{

  const int a = 10;

  //int& ra = a; //该语句编译时会出错,因为a为常量

  const int& ra = a;

  //int& b = 10; //该句编译时会出错,因为b为常量

  const int& b = 10;

  double b = 12.34;

  //int& rd = d; //该语句编译时会出错,因为类型不同

  const int& rd = d;

}

做返回值

正常情况下的传值返回:

int Count()

{

    int n = 0;

    n++;

    return n;

}

int main()

{

     int ret = Count();

     cout << ret << endl;

}

 如果将代码改成

int& Count()

{

    int n = 0;

    n++;

    return n;

}

int main()

{

     int ret = Count();

     cout << ret << endl;

     cout << ret << endl;

}

此时,相当于是Count函数的返回值是n的一个引用

虽然短期内可以返回正确的值

但是,我们知道,出了Count函数后,n变量就会被销毁,此时我们利用它的引用取访问它,其实就相当于之前的野指针问题,会有很大的安全问题。

如果将代码再改成下面的情况:

int& Count()

{

    int n = 0;

    n++;

    return n;

}

int main()

{

     int& ret = Count();

     cout << ret << endl;

     cout << ret << endl;

}

相当于ret和返回值都是n这个变量

第二次访问的时候出现了随机值。

因为cout输出本身也是调用了函数,在Count函数调用完后,它的栈帧销毁了(其中也包括n的那块空间),紧接着cout开始调用函数,这时,cout调用的函数的栈帧就可能会将之间的Count所在的栈帧进行覆盖,同理,n所在的那块栈帧就可能会被各种值覆盖,因此,第二次通过n的引用访问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) = " << ret << endl;

    return 0;

}

同样的道理,在不清栈帧的情况下,Add(1,2)函数执行的时候开辟的栈帧的c所在的空间,会被后面再次调用Add(3,4)时覆盖,得到7。

结论:

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

什么情况下可以使用传引用返回

1.返回的变量是全局对象(这样出了函数,变量并不会被销毁)

2.返回的对象是静态对象

传引用传参的作用(任何时候都可以)

1.提高效率

2.输出型参数(形参的修改,影响的实参)

传引用返回的作用(出了函数作用域对象还在才能用)

1. 提高效率

2.可以修改返回对象:

小例:单链表的访问和修改:

单链表:

struct SepList

{

     int *a;

     int size;

     int capacity;

};

c语言:

//访问第i个位置的值

int SLAT(struct SepList* ps, int i)

{

      assert(i < ps->size);

      return ps->a[i];

}

//将第i个位置的值修改为x

void SLModify(struct SepList* ps, int i, int x)

{

     assert(i < ps-> size);

     ps->a[i] = x;

}

c++引用:

//访问或修改第i个位置的值

int& STAT(struct SepList* ps, int i)

{

      assert(i < ps -> size);

      return ps->a[i];

}

此时,引用返回如果想要修改第i个位置的值,则直接修改即可,因为函数的返回是ps->a[i]的一个引用,所以此函数既可以访问,也可以修改。

STAT(ps,i) = 1;

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

小例:

int main()

{

     const int a = 0;

     //权限的放大:本来a变量不可以修改的,但是引用变量b是可以被修改的,所以这里不能这么写

      int& b = a;

    //权限的平移:这里b和a都不能被修改,所以权限是相同的,可以这样写

    const int& b = a;

 

   //权限的缩小:本来a是可以被修改的,但是引用变量b不可以,这样就会把a的权限缩小,但是这样是合法的

      int a = 0;

     const int& b = a;

      return 0;

}

还有一种情况:

int a = 7;

double& b = a;

这样写也是会报错的:

因为int类型的变量赋值给double时,不是直接给的,中间会有一个double类型的临时变量,也就是隐式转换,先将int类型的a转换成double类型,赋值给临时变量,然后将这个临时变量的值给b。

而我们知道,临时变量具有常量性,所以如果这样写,会有权限放大的现象。

如果改为:

int a = 7;

const double& b = a;

就不会报错了。

类似的:

int fun()

{

    int a = 0;

    return a;

}

int main()

{

     int& ret = fun();

     return 0;

}

这里也会报错:

因为,fun函数不是直接返回a,而是返回a的一份临时拷贝,也是具有常量性,所以这里的int& ret = fun()也存在权限放大的问题。

但是改为const int& ret = fun()就可以了。

这里的临时变量也会因为这里的引用被自动延长生命周期,直到不再使用为止。

引用和指针的联系

从汇编角度来看,引用其实和指针在底层其实是一种操作,也就是说底层其实没有引用,只有指针。

 

 执行++操作的步骤也是一样的。

引用和指针的区别

1.引用概念上定义一个变量的别名,指针存储了一个变量指针。

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

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

4.没有NULL引用,但又NULL指针。

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

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

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

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

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

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

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

相关文章

ES 7.6 - APi基础操作篇

ES7.6-APi基础操作篇 前言相关知识索引相关创建索引查询索引查询所有索引删除索引关闭与打开索引关闭索引打开索引 冻结与解冻索引冻结索引解冻索引 映射相关创建映射查看映射新增字段映射 文档相关(CURD)新增文档根据ID查询修改文档全量覆盖根据ID选择性修改根据条件批量更新 …

手写数字识别之网络结构

目录 手写数字识别之网络结构 数据处理 经典的全连接神经网络 卷积神经网络 手写数字识别之网络结构 无论是牛顿第二定律任务&#xff0c;还是房价预测任务&#xff0c;输入特征和输出预测值之间的关系均可以使用“直线”刻画&#xff08;使用线性方程来表达&#xff09…

【IMX6ULL驱动开发学习】10.Linux I2C驱动实战:AT24C02驱动设计流程

前情回顾&#xff1a;【IMX6ULL驱动开发学习】09.Linux之I2C框架简介和驱动程序模板_阿龙还在写代码的博客-CSDN博客 目录 一、修改设备树&#xff08;设备树用来指定引脚资源&#xff09; 二、编写驱动 2.1 i2c_drv_read 2.2 i2c_drv_write 2.3 完整驱动程序 三、上机测…

Spring 与【MyBatis 】和【 pageHelper分页插件 】整合

目录 一、Spring整合MyBatis 1. 导入pom依赖 2. 利用mybatis逆向工程生成模型层层代码 3. 编写配置文件 4. 注解式开发 5. 编写Junit测试类 二、AOP整合pageHelper分页插件 1. 创建一个AOP切面 2. Around("execution(* *..*xxx.*xxx(..))") 表达式解析 3. 编…

Visual Studio 2022的MFC框架——WinMain函数

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;今天我们来重新审视一下Visual Studio 2022下开发工具的MFC框架知识。 大家还记得创建Win32应用程序是怎么弄的吗&#xff1f; Win32应用程序的建立到运行是有一个个关系分明的步骤的&#xff1a; 1.进入W…

【面试经典150题】删除有序数组中的重复项-JavaScript版

题目链接 思路1&#xff1a;使用set。 /*** param {number[]} nums* return {number}*/ var removeDuplicates function(nums) {const uniqueSetnew Set();for(let i0;i<nums.length;i){uniqueSet.add(nums[i]);}const uniqueArrayArray.from(uniqueSet);nums.length0;nu…

【LeetCode75】第三十五题 统计二叉树中好节点的数目

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 给我们一棵二叉树&#xff0c;让我们统计这棵二叉树中好节点的数目。 那么什么是好节点&#xff0c;题目中给出定义&#xff0c;从根节点…

浅谈 Linux 下 vim 的使用

Vim 是从 vi 发展出来的一个文本编辑器&#xff0c;其代码补全、编译及错误跳转等方便编程的功能特别丰富&#xff0c;在程序员中被广泛使用。 Vi 是老式的字处理器&#xff0c;功能虽然已经很齐全了&#xff0c;但还有可以进步的地方。Vim 可以说是程序开发者的一项很好用的工…

门禁系统忘记登入密码,现在更换电脑如何迁移旧电脑门禁系统的数据

环境&#xff1a; ivms-4200 v3.10.0.6_c 问题描述&#xff1a; 门禁系统忘记登入密码,现在更换电脑如何迁移旧电脑门禁系统的数据&#xff0c;旧电脑记住密码&#xff0c;忘了密码和密保了 解决方案&#xff1a; 1.前往海康官网下载4200客户端&#xff0c;在新电脑上安装 …

平衡二叉树及其应用详解

平衡二叉树 定义与性质 平衡二叉树&#xff08;Balanced Binary Tree&#xff09;是计算机科学中的一种数据结构&#xff0c;它是二叉排序树的一种特殊情况。 平衡二叉树满足以下性质&#xff1a; 左子树和右子树的高度差不超过 1。也就是说&#xff0c;对于任意节点&#…

echarts 甘特图一组显示多组数据

<template><el-button type"primary" click"addlin">添加线</el-button><el-button type"success" click"addArea">添加区域</el-button><div ref"echart" id"echart" class&qu…

18-使用钩子函数判断用户登录权限-登录前缀

钩子函数的两种应用: (1). 应用在app上 before_first_request before_request after_request teardown_request (2). 应用在蓝图上 before_app_first_request #只会在第一次请求执行,往后就不执行, (待定,此属性没调试通过) before_app_request # 每次请求都会执行一次(重点…

计网-All

路由器的功能与路由表的查看_路由器路由表_傻傻小猪哈哈的博客-CSDN博客路由基础-直连路由、静态路由与动态路由的概念_MikeVane-bb的博客-CSDN博客路由器的功能与路由表的查看_路由器路由表_傻傻小猪哈哈的博客-CSDN博客 直连路由就是路由器直接连了一个网段&#xff0c;他就…

【C++ 学习 ⑱】- 多态(上)

目录 一、多态的概念和虚函数 1.1 - 用基类指针指向派生类对象 1.2 - 虚函数和虚函数的重写 1.3 - 多态构成的条件 1.4 - 多态的应用场景 二、协变和如何析构派生类对象 2.1 - 协变 2.2 - 如何析构派生类对象 三、C11 的 override 和 final 关键字 一、多态的概念和虚…

微信扫码跳转微信小程序

一:首先声明为什么需要这样做 项目中需要在后台管理页面进行扫码支付,其他人弄了微信小程序支付,所以就需要挑战小程序进行支付,在跳转的时候需要参数例如订单编号等 二:跳转小程序的方法有多种 接口调用凭证 | 微信开放文档 具体可以参考微信开放文档 1.获取scheme码 按照文…

【项目实战典型案例】05.前后端分离的好处(发送调查问卷)

目录 一、背景二、思路三、过程1、主要的业务逻辑2、解决问题的思路 四、总结五、面向对象的好处 一、背景 以下流程图是给用户发送调查问的整体流程&#xff0c;将不必要的业务逻辑放到前端进行处理。这样导致逻辑混乱难以维护。前后端分离的其中一个目的是将功能的样式放在了…

基础论文学习(5)——MAE

MAE&#xff1a;Masked Autoencoders Are Scalable Vision Learners Self-Supervised Learning step1&#xff1a;先用无标签数据集&#xff0c;把参数从一张白纸训练到初步预训练模型&#xff0c;可以得到数据的 Visual Representationstep2&#xff1a;再从初步成型&#x…

clickhouse ssb-dbgen数据构造 及 clickhouse-benchmark简单压测

一、 测试数据构造 1. 数据样例 官方文档有给出一批数据样例。优点是比较真实&#xff0c;缺点是太大了&#xff0c;动辄上百G不适合简单小测试 Anonymized Yandex.Metrica DatasetStar Schema BenchmarkWikiStatTerabyte of Click Logs from CriteoAMPLab Big Data Benchma…

浅析Linux 物理内存外碎片化

本文出现的内核代码来自Linux4.19&#xff0c;如果有兴趣&#xff0c;读者可以配合代码阅读本文。 一、Linux物理内存外碎片化概述 什么是Linux物理内存碎片化&#xff1f;Linux物理内存碎片化包括两种&#xff1a; 1.物理内存内碎片&#xff1a;指分配给用户的内存空间中未…

【产品规划】优先级规划

文章目录 1、功能优先级保障了产品在最短时间接受验证2、隐藏在优先级背后的是产品的目标和价值3、敏捷方法论中的功能优先级制定方法4、优先级制定时常见问题和应对方法5、敏捷方法论中的开发计划制定 1、功能优先级保障了产品在最短时间接受验证 2、隐藏在优先级背后的是产品…