【C++入门】引用

目录

6.引用

6.1引用概念 

6.2引用的写法 

6.3引用的特性

6.4常引用

6.5引用的使用场景

6.5.1引用做参数

6.5.2引用做返回值❗❗

🎇值做返回值

🎇引用做返回值

🎇引用在顺序表做返回值

6.5.3传值、传引用效率比较(参数)

6.5.4值和引用的作为返回值类型的性能比较(返回值)

6.6引用和指针

6.6.1引用替换指针的场景

6.6.2引用和指针的区别 


6.引用

6.1引用概念 

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

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

6.2引用的写法 

 引用的写法格式:

  • 类型& 引用变量名(对象名) = 引用实体;
  • 可以给变量的别名取别名,所有别名都是公用同一块内存空间。
  • 注意:引用类型必须和引用实体是同种类型的
//类型& 引用变量名(对象名) = 引用实体;
void TestRef()
{int a = 10;int& ra = a;//<====定义引用类型printf("%p\n", &a);printf("%p\n", &ra);
}

 【验证变量和变量的引用(别名)是否是同一块内存空间】

#include<iostream>
using namespace std;int main()
{int a = 0;int& b = a;int c = a;cout << &a << endl;cout << &b << endl;cout << &c << endl;cout << b << endl;cout << c << endl;b++;//b++的同时a++了吗?c++了吗?cout << a << endl;cout << b << endl;cout << c << endl;return 0;
}

【别名的别名&类型一致原则】 

int main()
{int a = 0;int& b = a;int& c = b;cout << &a << endl;cout << &b << endl;cout << &c << endl;//❌double& b=a;类型必须一致return 0;
}

6.3引用的特性

  • 1. 引用在定义时必须初始化
  • 2. 一个变量可以有多个引用
  • 3. 引用一旦引用一个实体,再不能引用其他实体

大家可以自己动手验证下。

int main()
{int a = 0;//1.引用必须初始化//int& b;//b = a;//2.引用定义后,不能改变指向int& b = a;int c = 2;b = c;//这是不是改变指向,而是赋值cout << a << endl;//2cout << b << endl;//2//3.一个变量可以有多个引用,多个别名//定义别名的别名int& x = a;int& d = c;return 0;
}

6.4常引用

void TestConstRef()
{const int a = 10;//int& ra = a;   // 该语句编译时会出错,a为常量const int& ra = a;// int& b = 10; // 该语句编译时会出错,b为常量const int& b = 10;double d = 12.34;//int& rd = d; // 该语句编译时会出错,类型不同const int& rd = d;
}

6.5引用的使用场景

6.5.1引用做参数

 引用做参数:

  • 输出型参数
  • 输入型参数
  • 对象比较大,减少拷贝,提高效率
  • 指针可以做,但是引用的更方便,更有性价比
//输出型参数
int* preorderTraversal(struct TreeNode* root, int* returnSize)
int* preorderTraversal(struct TreeNode* root, int& returnSize) 

//交换两个变量#include<iostream>
using namespace std;void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}
int main()
{int x = 10;int y = 20;cout << "x=" << x << endl;cout << "y=" << y << endl;Swap(&x, &y);cout << "x=" << x << endl;cout << "y=" << y << endl;return 0;
}void Swap(int& p1, int& p2)
{int tmp = p1;p1 = p2;p2 = tmp;
}
int main()
{int x = 10;int y = 20;cout << "x=" << x << endl;cout << "y=" << y << endl;Swap(x, y);cout << "x=" << x << endl;cout << "y=" << y << endl;return 0;
}
//不能交换地址

 

6.5.2引用做返回值❗❗

🎇值做返回值

 请问return a意味着把a的值传给func吗?回顾:函数栈帧的创建与销毁-CSDN博客

  • 函数调用结束,函数栈帧就销毁了,return需要返回值,会再函数栈帧销毁之前拷贝放到临时空间里面。给ret接收。 
  • 数据小,临时空间是寄存器。数据大。
#include<iostream>
using namespace std;
int func()
{int a = 0;return a;
}int main()
{int ret = func();return 0;
}


🎇引用做返回值
  • 函数调用结束,函数栈帧就销毁了,return返回a的别名,给ret接收。 
  • ret接收到了之后,后面访问a的别名,也就是访问a的空间,但是a的空间已被销毁,就会造成非法访问,野引用的问题。非法访问!!!特别注意
  1. 如果a的内存空间在函数调用之后没有被清理,去访问可能访问到原值。但是如果后序定义其他的变量,这个内存位置可能就被操作系统分配给其他的变量。
  2. 如果a的内存空间在函数调用之后被清理了,访问到了随机值。

  • 注意:如果函数返回时,出了函数作用域,如果返回对象还在(还没还给系统),则可以使用引用返回,如果已经还给系统了,则必须使用传值返回。
  • 返回变量出了函数作用域就生命周期(局部变量),不能用引用返回。返回变量出了函数作用域就生命周期到了就销毁,不能用引用返回。
  • 非法访问!!野引用!!对程序存在安全隐患。
  • C++中临时变量具有常量(不可被修改)

    全局变量/静态变量static/堆上变量等就可以引用做返回值。

【示例一】 

#include<iostream>
using namespace std;
int& func()
{int a = 0;//✔static int a=0;return a;
}int main()
{int ret = func();return 0;
}

【示例二】 

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


🎇引用在顺序表做返回值
  • 不需要typedef 就可以直接使用SeqList
  • 结构体(类)中可以包含成员函数
  • 域不同,不用再区分名称
  • 开辟的空间在堆上,出了作用域,函数栈帧没有销毁(可以用引用做返回值)

返回引用

  • 引用具有了读写双重功能
  • 引用中间没有产生临时变量

引用做返回值

  • 修改返回对象
  • 减少拷贝提高效率

这个部分到后面还会细讲

//C++
#include<iostream>
#include<assert.h>
using namespace std;
struct SeqList
{//成员变量int* a;int size;int capacity;//成员函数void Init(){a = (int*)malloc(sizeof(int) * 4);// ...size = 0;capacity = 4;}void PushBack(int x){//... 扩容a[size++] = x;}//读写返回变量int& Get(int pos){assert(pos >= 0);assert(pos < size);return a[pos];}int& operator[](int pos){assert(pos >= 0);assert(pos < size);return a[pos];}
};int main()
{SeqList s;s.Init();s.PushBack(1);s.PushBack(2);s.PushBack(3);s.PushBack(4);for (int i = 0; i < s.size; i++){//cout << s.Get(i)<< " ";cout << s[i] << " ";//cout << s.operator[](i) << " ";}cout << endl;for (int i = 0; i < s.size; i++){/*if (s.Get(i) % 2 == 0){s.Get(i) *= 2;}*/if (s[i] % 2 == 0){s[i] *= 2;}}cout << endl;for (int i = 0; i < s.size; i++){cout << s.Get(i) << " ";}cout << endl;return 0;
}

6.5.3传值、传引用效率比较(参数)

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

  • clock只能计算出整数位。0ms表示小于1ms,不代表没有,代表时间极其短。
#include <time.h>
struct A { int a[10000]; };
void TestFunc1(A a) {}
void TestFunc2(A& a) {}
void main()
{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;
}

6.5.4值和引用的作为返回值类型的性能比较(返回值)

通过上述下述代码的比较,发现传值和指针在作为传参以及返回值类型上效率相差很大。 

#include <time.h>
struct A { int a[10000]; };
A a;
// 值返回
A TestFunc1() { return a; }
// 引用返回
A& TestFunc2() { return a; }
void main()
{// 以值作为函数的返回值类型size_t begin1 = clock();for (size_t i = 0; i < 100000; ++i)TestFunc1();size_t end1 = clock();// 以引用作为函数的返回值类型size_t begin2 = clock();for (size_t i = 0; i < 100000; ++i)TestFunc2();size_t end2 = clock();// 计算两个函数运算完成之后的时间cout << "TestFunc1 time:" << end1 - begin1 << endl;cout << "TestFunc2 time:" << end2 - begin2 << endl;
}

 

6.6引用和指针

6.6.1引用替换指针的场景

  • 指针和引用的功能是类似的,重叠的。
  • C++的引用,对指针使用比较复杂的场景进行一些替换【场景替换】,让代码更简单易懂,但是不能完全替代指针。
  • C++的引用不能完全替代指针的元婴就有:引用定义之后,不能改变指向,但是指针可以改变指向。
  • 在Java python 等其他语言里没有指正,只有引用,且其他语言的引用是可以改变指向的实现链表等。注意区别C++的引用是不能改变指向的。
  • C++中90%的场景还是使用引用,但是在一些特殊场景,我们还是使用指针。(例如:链表,二叉树,指向一段动态内存开辟的空间)

【链表中二级指针】

//双向无头链表
typedef struct Node
{struct Node* next;struct Node* prev;int val;
}LNode,*PNode;//void PushBack(PNode* phead, int x)
void PushBack(PNode& phead, int x)
{//
}
int main()
{PNode pphead;PushBack(pphead, 1);
}

【输出型参数】

OJ输入型参数:直接给数据使用。

OJ输出型参数:给参数需要修改这个参数(指针/别名)

int* preorderTraversal(struct TreeNode* root, int* returnSize)
int* preorderTraversal(struct TreeNode* root, int& returnSize) 

6.6.2引用和指针的区别 

引用和指针的不同点:

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

语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。

int main()
{int a = 10;int& ra = a;cout << "&a = " << &a << endl;cout << "&ra = " << &ra << endl;return 0;
}

底层实现上实际是有空间的,因为引用是按照指针方式来实现的。 

int main()
{int a = 10;int& ra = a;ra = 20;int* pa = &a;*pa = 20;return 0;
}

引用和指针的区别:

【语法】

  • 引用是别名,不开空间;指针是地址,需要开空间存地址。
  • 引用必须初始化,指针可以初始化可以不用初始化。
  • 引用不能改变指向,指针可以改变指向。
  • 引用相对更加安全,没有空引用;但有空指针。
  • 容易出现野指针;但不容易出现野引用。
  • 其他方面。

【底层】

  • 汇编层面上,没有引用,只有指针,引用编译后也转换成指针了。

🙂感谢大家的阅读,若有错误和不足,欢迎指正。

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

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

相关文章

【OpenGL的着色器03】内置变量和函数(gl_Position等)

目录 一、说明 二、着色器的变量 2.1 着色器变量 2.2 着色器内置变量 三、最常见内置变量使用范例 3.1 常见着色器变量 3.2 示例1&#xff1a; gl_PointSize 3.3 示例2&#xff1a;gl_Position 3.4 gl_FragColor 3.5 渲染点片元坐标gl_PointCoord 3.6 gl_PointCoo…

Android Gradle开发与应用 (三) : Groovy语法概念与闭包

1. Groovy介绍 Groovy是一种基于Java平台的动态编程语言&#xff0c;与Java是完全兼容&#xff0c;除此之外有很多的语法糖来方便我们开发。Groovy代码能够直接运行在Java虚拟机&#xff08;JVM&#xff09;上&#xff0c;也可以被编译成Java字节码文件。 以下是Groovy的一些…

图像处理与视觉感知---期末复习重点(1)

文章目录 一、概述二、图像处理基础2.1 视觉感知要素2.2 像素间的一些基本关系2.2.1 相邻像素2.2.2 连通性2.2.3 距离度量 2.3 基本坐标变换2.4 空间变换与灰度值 一、概述 1. 图像的概念及分类。  图像是用各种观测系统以不同形式和手段观测客观世界而获得的、可以直接或间接…

nodejs版本管理工具nvm安装和环境变量配置

1、下载nvm.exe https://github.com/coreybutler/nvm-windows/releases2、安装 1.在D盘根目录新建一个dev文件夹&#xff0c;在dev里面再新建一个nodejs。 2.双击下载好的nvm.exe 修改文件路径&#xff0c;且路径中不能有中文 3.安装完成后在D:\dev\nvm打开settings.txt&…

kerberos学习系列一:原理

1、简介 Kerberos 一词来源于古希腊神话中的 Cerberus —— 守护地狱之门的三头犬。 Kerberos 是一种基于加密 Ticket 的身份认证协议。Kerberos 主要由三个部分组成&#xff1a;Key Distribution Center (即KDC)、Client 和 Service。 优势&#xff1a; 密码无需进行网络传…

Docker数据卷篇

1. 数据卷&#xff08;容器数据管理&#xff09; 引言&#xff1a;在之前的nginx案例中&#xff0c;修改nginx的html页面时&#xff0c;需要进入nginx内部。并且因为没有编辑器&#xff0c;修改文件也很麻烦。 这就是因为容器与数据&#xff08;容器内文件&#xff09;耦合带…

Scrapy与分布式开发(3):Scrapy核心组件与运行机制

Scrapy核心组件与运行机制 引言 这一章开始讲解Scrapy核心组件的功能与作用&#xff0c;通过流程图了解整体的运行机制&#xff0c;然后了解它的安装与项目创建&#xff0c;为后续实战做好准备。 Scrapy定义 Scrapy是一个为了爬取网站数据、提取结构性数据而编写的应用框架…

Claude3荣登榜首,亚马逊云科技为您提供先行体验!

Claude3荣登榜首&#xff0c;亚马逊云科技为您提供先行体验&#xff01; 个人简介前言抢先体验关于Amazon BedrockAmazon Bedrock 的功能 Claude3体验教程登录Amazon Bedrock试用体验管理权限详细操作步骤1.提交应用场景详细信息2.请求模型的访问权限3.请求成功&#xff0c;开始…

Mybatis实现分页查询数据(代码实操讲解)

在MyBatis中实现分页查询的常见方式有两种&#xff1a;使用MyBatis内置的分页插件如PageHelper&#xff0c;或者手动编写分页的SQL语句。下面我将为你提供两种方式的示例代码。 使用PageHelper分页插件 首先&#xff0c;确保你的项目中已经添加了PageHelper的依赖。在Maven项…

overleaf latex 笔记

overleaf: www.overleaf.com 导入.tex文件 1.代码空一行&#xff0c;代表文字另起一段 2. 1 2 3 排序 \begin{enumerate} \item \item \item \end{enumerate} 3.插入图片 上传图片并命名 \usepackage{float}导包\begin{figure}[H]&#xff1a;表示将图…

【网络安全】漏洞挖掘入门教程(非常详细),小白是如何挖漏洞(技巧篇)0基础入门到精通!

温馨提示&#xff1a; 初学者最好不要上手就去搞漏洞挖掘&#xff0c;因为漏洞挖掘需要很多的系统基础知识和一些理论知识做铺垫&#xff0c;而且难度较大…… 较合理的途径应该从漏洞利用入手&#xff0c;不妨分析一些公开的CVE漏洞。很多漏洞都有比较好的资料&#xff0c;分…

【四】【SQL Server】如何运用SQL Server中查询设计器通关数据库期末查询大题

数据库学生选择1122 数据库展示 course表展示 SC表展示 student表展示 数据库学生选课1122_3 第十一题 第十二题 第十三题 第十四题 第十五题 数据库学生选课1122_4 第十六题 第十七题 第十八题 第十九题 第二十题 数据库学生选课1122_5 第二十一题 第二十二题 结尾 最后&…

保留数据的重装系统教程!(win10系统)

上车警告&#xff01;&#xff01;&#xff01; 本教程无需思考&#xff0c;跟着操作一步一步来就能完成系统的重装。原理是将C盘系统重装&#xff0c;其他盘符数据保存。适用于系统盘重装数据或更改系统版本。 重要提示&#xff01;&#xff01;&#xff01; C盘有重要学习资…

Long-term Correlation Tracking LCT目标跟踪算法原理详解(个人学习笔记)

目录 1. 算法总览2. 算法详解2.1. 基础相关滤波跟踪2.2. 各模块详解2.2.1. 相关跟踪2.2.2. 在线检测器 3. 算法实现3.1. 算法步骤3.2. 实现细节 4. 相关讨论&总结 1. 算法总览 LCT的总体流程如上图所示&#xff0c;其思想为&#xff1a;将长时跟踪&#xff08;long-term tr…

第五节 JDBC驱动程序类型

JDBC驱动程序是什么&#xff1f; JDBC驱动程序在JDBC API中实现定义的接口&#xff0c;用于与数据库服务器进行交互。 例如&#xff0c;使用JDBC驱动程序&#xff0c;可以通过发送SQL或数据库命令&#xff0c;然后使用Java接收结果来打开数据库连接并与数据库进行交互。 JDK…

18.四数之和

题目&#xff1a;给你一个由 n 个整数组成的数组 nums &#xff0c;和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] &#xff08;若两个四元组元素一一对应&#xff0c;则认为两个四元组重复&#xff09;&#x…

每日一题 — 有效三角形的个数

611. 有效三角形的个数 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 双指针思想&#xff0c;先将数据排序 然后先固定最大的数Max&#xff0c;也就是数组最后的数再定义一左一右两个下标 left 、 right&#xff0c;当这个值相加大于最大的数&#xff0c;那么他两…

Pytorch学习 day05(RandomCrop、Transforms工具使用总结)

RandomCrop 将PIL或Tensor格式的输入图片&#xff0c;随机裁剪指定尺寸的部分输入尺寸可以为序列或单个整形数字代码如下&#xff1a; from PIL import Image from torchvision import transforms from torch.utils.tensorboard import SummaryWriterimg Image.open("i…

python高级之元类

python高级之元类 一、Type创建类1、传统方式创建类2、非传统方式 二、元类三、总结 一、Type创建类 class A(object):def __init__(self, name):self.name namedef __new__(cls, *args, **kwargs):data object.__new__(cls)return data根据类创建对象 objA(‘kobe’) 1、执…

外包干了2年,技术退步明显

先说一下自己的情况&#xff0c;研究生&#xff0c;19年进入广州某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试&#xf…