【C++】引用与指针

news2024/12/24 2:41:45

专栏放在【C++知识总结】,会持续更新,期待支持🌹


引用

引用的概念

在C++中,引用的本质其实就是给一个已经存在的变量”起别名“。也就是说,引用与它所引用的对象共用一块空间。(同一块空间的多个名字

就比如说,李逵又叫黑旋风,而黑旋风就是指李逵本人,只是名字换了而已。

引用的特性

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

2. 一个变量可以有多个引用,但一个引用只能有一个实体对象

#include<iostream>
using namespace std;
int main()
{
    int a = 0;
    //一个变量可以有多个引用,但一个引用只能有一个实体
    int& b = a;//不可以写成int& b;  引用必须在定义时初始化
    int& c = b;
    int& d = c;

    cout << &a << endl;//012FFBD0
    cout << &b << endl;//012FFBD0
    cout << &c << endl;//012FFBD0
    cout << &d << endl;//012FFBD0
    //地址相同,abcd共用同一块空间
    // 另外,引用类型与引用实体的类型必须一致,这里不能写为char & d = a(error)
    return 0;
}

扩展(函数栈帧的创建与销毁)

这里我们进行复习一下关于函数栈帧的一些知识。我们知道,在调用一个函数时, 首先会在内存占用一块空间,用来创建该函数的函数栈帧,当调用结束后,该函数栈帧会被销毁,这里需要注意的是,当 栈帧被销毁后,这里的空间实际上在内存中还是存在的,只不过空间的使用权不再归我们使用并且函数栈帧的销毁,可能会对原有空间进行清理。
这里可以举个例子来理解一下,就好比说我们在酒店开了一个房间,并且在退房时把我们的电脑放在了房间里,在这里,酒店就相当于内存的存在,而我们退房的那一刻,就好比 函数栈帧销毁的那一刻,但是虽然我们退房了,该房间还是实际存在的,并没有说随着我们的退房而消失,只不过不再归我们使用。并且房间里的东西也 可能会被清理(也可能依然还在),加入此时我们再进行使用该房间,用是可以用,只不过肯定是不合法的,这种行为就好比 空间的非法访问。

引用的使用场景

做参数进行引用(输出型参数)

所谓输出型参数,实际上就是可以影响实参的参数,就比如我们经常写的交换两个变量的值,在以前我们会使用指针来完成传址调用,从而实现形参的改变影响实参,但现在我们可以用引用来实现,如下:
//做参数来使用,由于共用同一块空间,所以这里的c实际上就是a,d实际就是b
void Swap(int& c, int& d)
{
    int tmp = c;
    c = d;
    d = tmp;
}
int main()
{
    int a = 1, b = 2;
    Swap(a, b);
    cout << a << " " << b << endl;//2 1
    return 0;
}

可以做返回值使用

我们先来看这样一段代码:

这里注意的是:这里的a是局部变量,生命周期会随着栈区的销毁而结束,所以这里返回的实际上并不是a,我们通过查看反汇编发现实际上是借助了一个临时变量来实现的。

那么不禁会有个疑问,假如这里的a不随着栈帧的结束而销毁,那么会不会直接返回a呢?可以试验一下:

对于这种现象,我们可以把引用作为返回值来使用,从而实现优化,写成如下格式:

//返回值
int& Test()
{
    static int a = 10;
    a++;
    return a;//也会产生临时变量,但是临时变量的类型是int& 也就是a的别名,即临时变量就是返回的a,减少了拷贝操作
}
int main()
{
    int ret = Test();
    return 0;
}

这就是引用返回,即在返回类型前面加上&,虽然也需要借助临时变量的存在,但是由于临时变量的类型为int& ,即临时变量就是a,所以就减少了临时变量的拷贝工作,会使效率得到提升。我们可以来验证一下:

传值返回 vs 传引用返回效率对比

#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 < 1000000; ++i)
        TestFunc1();
    size_t end1 = clock();
    // 以引用作为函数的返回值类型
    size_t begin2 = clock();
    for (size_t i = 0; i < 1000000; ++i)
        TestFunc2();
    size_t end2 = clock();
    // 计算两个函数运算完成之后的时间
    cout << "TestFunc1 time:" << end1 - begin1 << endl;
    cout << "TestFunc2 time:" << end2 - begin2 << endl;
}

int main()
{
    TestReturnByRefOrValue();
}

运行结果如下:

我们发现传引用返回对比传值返回,效率会有显著提高(作为参数使用时,传引用参数的效率也会高于传值作为参数的效率)

当然,传引用作为返回值的使用是有一定的限制的,我们发现上面的代码能使用传引用返回的原因在于,返回的变量不会随着作用域的销毁而销毁假如说返回的对象出了作用域后已经销毁,则必须使用传值返回,否则返回的结果是不确定的!

并且引用用作返回时,还可以修改返回对象(后面的学习会用到很多,这里简单介绍)

如下:

#define N 10
typedef struct ARR
{
    int arr[N];
}ARR;

int& PosARR(ARR& arr,int i)
{
    return arr.arr[i];//这里的arr就是main函数里的arr,不会随着PosARR函数的结束而销毁,所以可以用引用返回
}
int main()
{
    ARR arr;
    for (int i = 0; i < N; i++)
    {
        PosARR(arr, i) = i * 10;//引用返回可以修改返回对象,这里的返回对象为arr.arr[i],对此进行修改
    }
    for (int i = 0; i < N; i++)
    {
        cout << arr.arr[i] << " ";//0 10 20 30 40 50 60 70 80 90
    }
    return 0;
}

总结

引用可以用作参数来使用(输出型参数),也可以用作返回使用,用作返回使用时返回的对象必须是出了所在函数作用域后不会销毁的(比如static修饰的变量,全局变量,malloc......),并且引用返回时,返回的对象可以被修改。同时还可以减少拷贝提高效率。

常引用

我们要记住这样一句话:指针和引用在赋值或者初始化时,权限可以被缩小或者保持,但不可进行修改。

这是什么意思呢?通过以下代码进行了解:

    // 权限放大(error)
    //const int c = 2;//const 修饰的常量不可以进行修改,可以理解只具有读的属性,不具有写的属性,而d可以修改,所以权限被放大
    //int& d = c;//这里正确写法应为const int& d=c;

    //const int* p1 = NULL;
    //int* p2 = p1;//同上,前面加个const即可,const int* p2=p1; (√)

    // 权限保持
    const int c = 2;
    const int& d = c;

    const int* p1 =NULL;
    const int* p2 = p1;

    // 权限缩小
    int x = 1;//x可以进行修改,可以理解为具有读和写的属性,而x是const修饰的,只具有读的属性,权限缩小了
    const int& y = x;

    int* p3 = NULL;
    const int* p4 = p3;//同上

不仅如此,由于所谓临时变量具有常性(即不可被修改)的原因,也会出现以下的情况:

    int i = 0;
    //double& p = i;//error
    //由于int到double类型发生类型转换,而类型转换会产生临时变量,临时变量又具有常性(只可读)
    //因此在前面加上const即可
    const double& p=i;//(√)

这也就解释了上文说到的引用类型与引用实体的类型必须一致,同样,在函数中也一样适用:

int add(int x, int y)
{
    int c = x + y;
    return c;//实际上是借助临时变量,将c拷贝给临时变量,再将临时变量拷贝给p
}
int main()
{
    int a = 1, b = 2;
    //int& p = add(a, b);//由于临时变量具有常性的特点,所以不可以这样写
    //前面加上const 即可
    const int& p = add(a, b);

    return 0;
}

引用与指针

&是一个很熟悉的符号,与指针有关,用在变量前面就是取地址符号,用在类型后面则为引用符号,那么指针与引用之间是否有着什么关系呢?

int a=0;
int* p=&a;//&:取地址符
int& b=a;//&:引用

指针与引用的相同点

实际上,引用与指针,两者之间在底层实现上其实是一样的,我们可以来进行验证

当然,两者之间也存在着很大的区别。

指针与引用的不同点

首先就是在语法概念上的区别, 引用只是同一个实体的不同名称, 不会单独开辟空间,但是指针会在内存开辟一块4/8byte大小的空间。
引用在定义时 必须初始化,指针没有要求
引用在初始化时引用一个实体后,就 不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体(这一点也就意味着 引用并不能实现完全替代指针,就比如在链表这里,用来指向下一个节点的变量类型,只能是指针)
有多级指针,但是没有多级引用
引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
引用比指针使用起来相对更安全(野指针)

...

看法:

因此对于指针与引用,我们只能是说引用相较于指针来说,更加容易理解使用,并且也不会存在空引用的问题,但是在一些场景下,引用自身的特点(不能改变指向)也存在着使用限制,此时就得用指针来实现

end

生活原本沉闷,但跑起来就会有风!🌹

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

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

相关文章

【搭建 mybatis 开发环境】

搭建 mybatis 开发环境 环境准备 创建数据库 创建maven 工程 pom文件&#xff1a;添加打包方式为jar 以及 mybatis 和 mysql 依赖 环境搭建 编写实体类 User.java&#xff1a;属性&#xff0c;生成get 和 set 方法 以及 toString方法 创建 IUserDao 接口&#xff0c;操作…

java进阶—一篇文章搞懂set 集合 及其底层实现

上节我们知道了List 下的两大 子类 ArrayList 跟 linkedList ArrayList 数组结构 查询快&#xff0c;增删慢 LinkedList 链表结构 查询慢&#xff0c;增删快 来看看我们今天的主角: Set Set 是 不可重复的&#xff0c;其底下也有两大子接口&#xff1a; HashSet&#xff1…

我用Python轻松玩转ChatGPT的聊天机器人和AI画画

ChatGPT是由人工智能研究实验室OpenAI在2022年11月30日发布的全新聊天机器人模型&#xff0c;一款人工智能技术驱动的自然语言处理工具。它能够通过学习和理解人类的语言来进行对话&#xff0c;还能根据聊天的上下文进行互动&#xff0c;真正像人类一样来聊天交流&#xff0c;甚…

分享112个图片切换,总有一款适合您

分享112个图片切换&#xff0c;总有一款适合您 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c; 112个图片切换下载链接&#xff1a;https://pan.baidu.com/s/1Q6DzF8wIQ9rXwFjydI6_kw?pwdktxr 提取码&#xff1a;kt…

Leetcode.1145 二叉树着色游戏

题目链接 Leetcode.1145 二叉树着色游戏 Rating &#xff1a; 1741 题目描述 有两位极客玩家参与了一场「二叉树着色」的游戏。游戏中&#xff0c;给出二叉树的根节点 root&#xff0c;树上总共有 n个节点&#xff0c;且 n为奇数&#xff0c;其中每个节点上的值从 1到 n各不相…

关于“智能出价“的一些探索和实践

背景在移动数字广告营销的整个链路中&#xff0c;主要有三个主体&#xff0c;分别是媒体、adx和dsp.对于一个adx系统&#xff0c;主要有两个功能&#xff0c;分别是&#xff1a;向下对接媒体&#xff1a;汇聚下游的媒体广告流量向上对接dsp(广告主)&#xff1a;将媒体流量卖给上…

【C语言】看了这篇文章,如果你还不会文件操作的话,我把这篇文章给吃了(doge)

&#x1f6a9;write in front&#x1f6a9; &#x1f50e;大家好&#xff0c;我是謓泽&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f3c5;2021年度博客之星物联网与嵌入式开发TOP5&#xff5…

MyBatis注解CRUD执行流程剖析

MyBatis Study Notes Day03 结果映射ResultMap 引入resultMap–MyBatis中最强大的元素 数据库字段名&#xff1a;&#xff1a; 实体类字段名&#xff1a; public class User {private int id;private String name;private String password;如上所示&#xff0c;当sql的字段…

动态规划详解(1)——基础概念

动态规划是数学、编程中一个重要的算法动态规划&#xff08;Dynamic Programming&#xff0c;DP&#xff09;是运筹学的一个分支&#xff0c;是求解决策过程最优化的过程。20世纪50年代初&#xff0c;美国数学家贝尔曼&#xff08;R.Bellman&#xff09;等人在研究多阶段决策过…

Java——根据身高重建队列

题目链接 leetcode在线oj题——根据身高重建队列 题目描述 假设有打乱顺序的一群人站成一个队列&#xff0c;数组 people 表示队列中一些人的属性&#xff08;不一定按顺序&#xff09;。每个 people[i] [hi, ki] 表示第 i 个人的身高为 hi &#xff0c;前面 正好 有 ki 个…

Spring AOP表达式(execution)规则——排除切点的应用

背景 需要项目原切面的基础上排除一些类中方法。 本篇文章主要介绍了SpringBoot AOP Pointcut切入点表达式&#xff0c;以及如何排除某些类中的方法的方式。 execution(* com.winup.web.controller..*.*(..)) 参数说明 符号含义execution&#xff08;&#xff09;表达式的…

【C++之类和对象】默认成员函数

目录前言一、默认成员函数二、构造函数三、析构函数四、拷贝构造函数五、赋值运算符重载前言 前面我们学习了一些类和对象的基本知识&#xff0c;知道了什么是类&#xff0c;类中包括什么东西&#xff0c;以及能够使用一个类来实例化对象&#xff0c;并且会计算类对象的大小。这…

Java Collection 接口下的 “ List 集合” 与 “ Set 集合 ”

Java Collection接口下的“ List 集合” 与 “ Set 集合 ” 每博一文案 一个人最好的底牌&#xff0c;就这两个字: 靠谱,是最高级的聪明。 师父说&#xff1a;人生一回&#xff0c;道义一场&#xff0c;你对人对事的态度&#xff0c;藏着你一生的福报。 千金难买好人缘&#x…

SpringBoot(三):日志文件

目录一、日志文件1.1 日志文件的作用1.2 Spring Boot内置了日志框架1.3 日志的格式说明1.4 自定义日志打印1.5 日志的持久化1.6 日志的级别1.6.1 日志级别有什么作用1.6.2 日志的级别划分1.6.3 日志级别的设置1.7 使用lombok输出日志1.7.1 lombok的原理1.7.2 lombok其他注解一、…

在JS文件中使用或扩展已有的vue文件

工作中遇到一个给现有项目增加一个超时重新登录的提醒框&#xff08;可在提醒框中直接登录本账户&#xff09;。 由于页面稍微复杂&#xff0c;本人又是脚手架一把梭过来的&#xff0c;对于直接使用 js 来完成一整个复杂还带逻辑的页面稍显吃力&#xff0c;所以决定先写一个 vu…

建模助手【有求必应】的正确打开方式

今天的话题主要想解除大家对[有求必应] 的一些误解。 因为在日常的反馈中用户似乎对于[提需求] 这玩意儿无论是从概念上还是动作上都很不 “熟悉”。 其实我们对软件认知的上限是一个软件功能的上限&#xff0c;产品以及行业的发展都要从打破固有认知开始。 期待更多的你们跳出…

《新华日报·科技周刊》聚焦蓝海彤翔与《流浪地球2》

瞄准世界科技前沿瞄准江苏科技创新瞄准日常科技生活《新华日报科技周刊》第203期聚焦《流浪地球2》中的大国重器其实就在我们身边重点报道了蓝海创意云渲染农场为《流浪地球2》提供了云计算渲染服务的重要成果“数字生命计划”就是元宇宙吗&#xff1f;电影中人类面临末日危机&…

0基础如何入门人工智能?

1.1 概念 根据维基百科的解释&#xff0c;人工智能是被机器展示的智力&#xff0c;与人类和其他动物的自然智能相反&#xff0c;在计算机科学中 AI 研究被定义为 “代理人软件程序”&#xff1a;任何能够感受周围环境并且能最大化它成功机会的设备。 1.2 重大事件 2016 年 3…

[Java]JavaWeb学习笔记(尚硅谷2020旧版)

文章目录&#x1f3c0; 视频及资料地址&#x1f3c0; XML⚽ XML 简介&#x1f3d0; xml 的作用⚽ XML 语法&#x1f3d0; 文档声明&#x1f3d0; 注释&#x1f3d0; 元素(标签)⚾ XML 命名规则⚾ xml 中的元素(标签)也分单双标签&#x1f3d0; xml 元素属性&#x1f3d0; 语法规…

PHP控制反转和依赖注入的理解(通俗易懂)

目录 1.IoC是什么 2.IoC能做什么 3.IoC和DI 4.IoC(控制反转) 5.DI(依赖注入) 6.我对IoC(控制反转)和DI(依赖注入)的理解 学习PHP各个框架的过程中&#xff0c;都会听过IoC(控制反转) 、DI(依赖注入)这两个概念&#xff0c;总觉得IoC 、DI这两个概念是模糊不清的&#xff…