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.引用比指针使用起来相对更安全。

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

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

相关文章

C#,《小白学程序》第一课:初识程序

曰&#xff1a;扫地僧练就绝世武功的目的是为了扫地更干净。 1 引言 编程只是一项技术&#xff0c;如包包子&#xff0c;不是什么高深的科学。 学习程序最不好的方法是先学习枯燥的语法。 学习程序主要是用代码解决问题。因此&#xff0c;我们抛开所有的语法与诸多废物&…

sql:SQL优化知识点记录(四)

&#xff08;1&#xff09;explain之ref介绍 type下的ref是非唯一性索引扫描具体的一个值 ref属性 例如&#xff1a;ti表先加载&#xff0c;const是常量 t1.other_column是个t1表常量 test.t1.ID&#xff1a;test库t1表的ID字段 t1表引用了shared库的t2表的col1字段&#x…

HTML之VSCode简单配置与创建

目录 插件下载 然后输入源码&#xff1a; 使用 效果 插件下载 下载这个插件后可以直接运行&#xff1a; 然后创建一个文件&#xff1a; 然后输入源码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"…

几个nlp的小任务(机器翻译)

几个nlp的小任务(机器翻译) 安装依赖库数据集介绍与模型介绍加载数据集看一看数据集的样子评测测试数据预处理测试tokenizer处理目标特殊的token预处理函数对数据集的所有数据进行预处理微调预训练模型设置训练参数需要一个数据收集器,把处理好数据喂给模型设置评估方法参数…

Python“牵手”淘宝商品列表数据,关键词搜索淘宝API接口数据,淘宝API接口申请指南

淘宝平台API接口是为开发电商类应用程序而设计的一套完整的、跨浏览器、跨平台的接口规范&#xff0c;淘宝API接口是指通过编程的方式&#xff0c;让开发者能够通过HTTP协议直接访问淘宝平台的数据&#xff0c;包括商品信息、店铺信息、物流信息等&#xff0c;从而实现淘宝平台…

Linux网络编程:线程池并发服务器 _UDP客户端和服务器_本地和网络套接字

文章目录&#xff1a; 一&#xff1a;线程池模块分析 threadpool.c 二&#xff1a;UDP通信 1.TCP通信和UDP通信各自的优缺点 2.UDP实现的C/S模型 server.c client.c 三&#xff1a;套接字 1.本地套接字 2.本地套 和 网络套对比 server.c client.c 一&#xff1a;线…

taro react/vue h5 中的上传input onchange 值得区别

<inputclassNamebase-input-file-h5typefileacceptimage/*capturecameraonChange{onChangeInput} />1、taro3react 2、taro3vue3

项目透明度如何改善团队的工作流程?

无论项目简单还是复杂&#xff0c;项目透明度一直是项目过程中的重要因素。在当今快节奏的商业环境中&#xff0c;对透明度的关注与日俱增&#xff0c;现已成为团队及其项目成功的关键因素。 但创建一个透明的流程对团队管理而言&#xff0c;是一个重大挑战&#xff0c;因团队…

C语言数值表示——进制、数值存储方式

进制 进制也就是进位制&#xff0c;是人们规定的一种进位方法对于任何一种进制—X进制&#xff0c;就表示某一位置上的数运算时是逢X进一位 十进制是逢十进一&#xff0c;十六进制是逢十六进一&#xff0c;二进制就是逢二进一&#xff0c;以此类推&#xff0c;x进制就是逢x进位…

Mysql001:Mysql概述以及安装

前言&#xff1a;本课程将从头学习Mysql&#xff0c;以我的工作经验来说&#xff0c;sql语句真的太重要的&#xff0c;现在互联网所有的一切都是建立在数据上&#xff0c;因为互联网的兴起&#xff0c;现在的数据日月增多&#xff0c;每年都以翻倍的形式增长&#xff0c;对于数…

微服务系统面经之二: 以秒杀系统为例

16 微服务与集群部署 16.1 一个微服务一般会采用集群部署吗&#xff1f; 对于一个微服务是否采用集群部署&#xff0c;这完全取决于具体的业务需求和系统规模。如果一个微服务的访问压力较大&#xff0c;或者需要提供高可用性&#xff0c;那么采用集群部署是一种常见的策略。…

Linux(实操篇一)

Linux实操篇 Linux(实操篇一)1. 常用基本命令1.1 帮助命令1.1.1 man获得帮助信息1.1.2 help获得shell内置命令的帮助信息1.1.3 常用快捷键 1.2 文件目录类1.2.1 pwd显示当前 工作目录的绝对路径1.2.2 ls列出目录的内容1.2.3 cd切换目录1.2.4 mkdir创建一个新的目录1.2.5 rmdir删…