【浅尝C++】引用

news2024/11/19 17:44:25

在这里插入图片描述

🎈归属专栏:浅尝C++
🚗个人主页:Jammingpro
🐟记录一句:大半夜写博客的感觉就是不一样!!


文章前言:本篇文章简要介绍C++中的引用,每个介绍的技术点,在可能的情况下,都附上代码了。


文章目录

  • 引用的概念
  • 引用的特征
  • 常引用
  • 使用场景
    • 做参数
    • 做返回值
  • 值传递与引用传递效率比较
  • 引用和指针的区别


引用的概念

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间
就跟现实生活中一样,一个人原名叫做光头强,再给他取了个别名叫帅靓仔。一样地,我们为a变量申请一个空间地址为0x00001010,再为a变量取一个别名叫做superA。此时,a和superA指向的内存地址均是0x00001010。
在这里插入图片描述
引用的语法为:变量类型& 引用变量名(对象名) = 引用实体;
下面给出引用语法的示例代码,并演示引用实体及引用变量指向同一内存空间。

#include <iostream>
using namespace std;

int main()
{
	int a = 100;
	int& superA = a;
	cout << "a's address is " << &a << endl;
	cout << "superA's address is " << &superA << endl;
	return 0;
}

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

引用的特征

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

对于上面3个属性,给出如下代码进行验证👇

#include <iostream>
using namespace std;

int main()
{
	int age = 18;
	// int& refer_age1;	//该代码编译不通过,引用在定义时必须初始化
	int& refer_age2 = age;
	int& refer_age3 = age;
	//这里说明了,一个变量可以有多个引用
	//age变量有refer_age2和refer_age3两个引用。当然,还可以设置更多age的引用
	int newAge = 20;
	refer_age2 = newAge;//这行代码可以正常运行,但这里不是将refer_age2变为newAge的引用,而是将age的值赋值为newAge的值
	
	cout << "age's value is " << age << " and its address is " << &age << endl;
	cout << "refer_age2's value is " << refer_age2 << " and its address is " << &refer_age2 << endl;
	cout << "newAge's value is " << newAge << " and its address is " << &newAge << endl;
	return 0;
}

上面代码运行后,agerefer_age2的地址相同,agerefer_age2newAge的值均为20。refer_age2 = newAge并不是将refer_age2设置为newAge的引用,而是将refer_age2指向的引用实体的值设置为newAge的值。这行语句的效果等同于age = newAge。因此,引用一旦引用一个实体,再不能引用其他实体。

常引用

我们知道,常量的值是不可以修改的。因此,常量的引用也不可以被赋值,即不能被修改数值。

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

上面代码中,a是常量,因而,其引用必须为const类型的引用。也就是说,引用的操作权限不能高于被引用实体的操作权限(即a不能被修改,则其引用也一定不能被修改)。同理,10是字面常量,因此,引用它的b也必须是常量。将int类型的引用指向double类型的引用实体,此时会发生类型转换,即double类型会被转换为int类型。转换时,编译器会尝试创建一个const int的临时对象,并将double类型转换为const int类型。因此,若给引用赋予引用实体时需要类型转换,该引用也必须为const类型。
在这里插入图片描述

使用场景

做参数

在没有引用之前,我们需要使用地址传递才能实现swap函数。如下面的代码👇

#include <iostream>
using namespace std;

void swapByAddress(int* left, int* right)
{
	int temp = *left;
	*left = *right;
	*right = temp;
}

int main()
{
	int num1 = 10, num2 = 20;
	cout << "num1 = " << num1 << ", num2 = " << num2 << endl;
	swapByAddress(&num1, &num2);
	cout << "num1 = " << num1 << ", num2 = " << num2 << endl;
	return 0;
}

有了引用之后,我们就不再需要使用取地址符来传递变量地址,并在给变量赋值时使用解引用运算符了。实现代码如下👇

#include <iostream>
using namespace std;

void swapByReference(int& left, int& right)
{
	int temp = left;
	left = right;
	right = temp;
}

int main()
{
	int num1 = 10, num2 = 20;
	cout << "num1 = " << num1 << ", num2 = " << num2 << endl;
	swapByReference(num1, num2);
	cout << "num1 = " << num1 << ", num2 = " << num2 << endl;
	return 0;
}

做返回值

与引用做参数一样,如果返回值为指针类型,后续需要对指针进行操作,整体操作比较繁琐。我们可以选择使用引用做返回值。代码如下👇

#include <iostream>

int& addGongDe()
{
	static int gongde = 0;
	++gongde;
	return gongde;
}

int main()
{
	int myGongDe = addGongde();
	cout << myGongde << endl;
	return 0;
}

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

下面给出一段运行时出错的代码👇

#include <iostream>
using namespace std;

int& Add(int a, int b)
{
	int c = a + b;
	return c;
}
int main()
{
	int& ret = Add(1, 2);
	cout << "Add(1, 2) is : " << ret << endl;
	return 0;
}

main函数调用Add函数时,栈中增加了abc三个变量,在Add函数返回后,这三个变量的内存空间被释放。在输出时,ret访问了已经释放的内存空间,故出现错误。

值传递与引用传递效率比较

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

当传递的参数数据量较大时,可以发现:引用传递效率明显高于值传递。下面为该结论的验证代码👇

#include <iostream>
#include <time.h>
using namespace std;

struct Ming{ int a[10000]; };

void Func1(Ming a){}

void Func2(Ming& a){}

void TestRefAndValue()
{
	Ming a;
 	//以值作为函数参数
 	size_t begin1 = clock();
	 for(size_t i = 0; i < 10000; ++i)
	     Func1(a);
	 size_t end1 = clock();

	 //以引用作为函数参数
	 size_t begin2 = clock();
	 for(size_t i = 0; i < 10000; ++i)
	     Func2(a);
	 size_t end2 = clock();

	 //分别计算两个函数运行结束后的时间
	 cout << "Func1(a)->time:" << end1 - begin1 << endl;
	 cout <<  "Func2(s)->time:" << end2 - begin2 << endl;
}

int main()
{
	TestRefAndValue();
	return 0;
}

不仅在传递函数参数时可以使用引用传递,引用还可以作为返回值。一样地,在数据量较大时,引用做返回值的效率明显高于值传递做返回值。下面为验证该结论的代码👇

#include <iostream>
#include <time.h>
using namespace std;

struct Ming{ int a[10000]; };

Ming a;
//值返回
Ming Func1(){ return a; }
//引用返回
Ming& Func2(){ return a; }

void TestReturnByRefOrValue()
{
	//以值作为函数的返回值类型
	size_t begin1 = clock();
	for(size_t i = 0; i < 100000; ++i)
	   Func1();
	size_t end1 = clock();
	//以引用作为函数的返回值类型
	size_t begin2 = clock();
	for(size_t i = 0; i < 100000; ++i)
	   Func2();
	size_t end2 = clock();

	//计算两个函数运算完成之后的时间
	cout << "Func1()-time : " << end1 - begin1 << endl;
	cout << "Func2()-time : " << end2 - begin2 << endl;
}

int main()
{
	TestReturnByRefOrValue();
	return 0;
}

从上面的文字和代码中,我们可以总结出:在程序能正常运行的前提下,传递函数参数及函数返回值时,尽量使用引用传递。

引用和指针的区别

在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用一块空间。在底层实现上实际是有空间的,因为引用是按照指针方式来实现的。

int main()
{
	int a = 10;

	int& refer_a = a;
	refer_a = 20;
	
	int* point_a = &a;
	*point_a = 20;
	
	return 0;
}

引用和指针的不同点:

  1. 引用概念上定义一个变量的别名,指针存储一个变量地址。
  2. 引用在定义时必须初始化,指针没有要求
  3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何 一个同类型实体
  4. 没有NULL引用,但有NULL指针 【引用必须初始化】
  5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32 位平台下占4个字节)
void test()
{
	long long num = 16;
	long long& refer_num = num;
	long* point_num = &num;
	
	cout << sizeof(refer_num) << endl;	//等于sizeof(long long)
	cout << sizeof(point_num) << endl;	//输出结果为指针所占内存空间大小
}
  1. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
void test()
{
	int arr[10] = {0};
	for(size_t i = 0; i < 10; i++)
	{
		arr[i] = i * 2;
	}
	int& refer_arr1 = &arr[1];
	int* point_arr1 = &arr[1];
	++refer_arr1;
	++point_arr1;
	cout << refer_arr1 << endl;		//输出结果为2
	cout << (*point_arr1) << endl;	//输出结果为4
}
  1. 有多级指针,但是没有多级引用
  2. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
void test()
{
	int num = 16;
	int& refer_num = num;
	int* point_num = &num;
	
	cout << refer_num << endl;		//不需要解引用
	cout << (*point_num) << endl;	//解引用操作
}
  1. 引用比指针使用起来相对更安全

文章结语:这篇文章对C++中的引用进行了简要的介绍。
🎈欢迎进入浅尝C++专栏,查看更多文章。
如果上述内容有任何问题,欢迎在下方留言区指正b( ̄▽ ̄)d

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

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

相关文章

leetcode面试经典150题——50 快乐数

题目&#xff1a;快乐数 描述&#xff1a; 编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为&#xff1a; 对于一个正整数&#xff0c;每一次将该数替换为它每个位置上的数字的平方和。 然后重复这个过程直到这个数变为 1&#xff0c;也可能是 无限循环 但始终变…

CSS 雷达监测效果

<template><view class="center"><view class="loader"><view></view></view></view></template><script></script><style>/* 设置整个页面的背景颜色为深灰色 */body {background-col…

【数据结构 | 希尔排序法】

希尔排序法 思路ShellSort 思路 希尔排序法又称缩小增量法。希尔排序法的基本思想是&#xff1a;先选定一个整数&#xff0c;把待排序文件中所有记录分成个组&#xff0c;所有距离为的记录分在同一组内&#xff0c;并对每一组内的记录进行排序。然后&#xff0c;取&#xff0c…

10分钟快速上手LLM大模型Python前端开发(一)

10分钟快速上手LLM大模型Python前端开发&#xff08;一&#xff09; 安装小试牛刀测试demo更改服务端口 github&#xff1a;https://github.com/riverind CSDN&#xff1a;https://blog.csdn.net/woai8339/article/details/131442801 微信公众号&#xff1a;leetcode_algos_lif…

C++PythonC# 三语言OpenCV从零开发(1):环境配置

文章目录 前言课程选择环境配置PythonC#COpenCV官网下载新建C项目测试运行Csharp版Python版 gitee仓库总结 前言 由于老王我想转机器视觉方向的上位机行业&#xff0c;我就打算开始从零学OpenCV。但是目前OpenCV有两个官方语言&#xff0c;C和Pyhont。C# 有大佬做了对应的Open…

AD20 解决PCB铺铜与锡盘之间锯齿状连接问题的设置方法

上一篇文章&#xff1a;PCB简单绘制一般步骤 对上一篇文章中&#xff0c;关于铺铜设置的补充&#xff0c;解决铺铜与锡盘之间的锯齿状连接情况。 1、新建Demo&#xff0c;创建PCB板子&#xff0c;布置锡盘和铺铜&#xff0c;如图&#xff1a; 2、设置规则&#xff0c;参考上一…

八. 实战:CUDA-BEVFusion部署分析-导出带有spconv的SCN网络的onnx

目录 前言0. 简述1. 使用spconv进行SCN的推理测试2. 导出onnx3. 补充-装饰器钩子函数总结下载链接参考 前言 自动驾驶之心推出的 《CUDA与TensorRT部署实战课程》&#xff0c;链接。记录下个人学习笔记&#xff0c;仅供自己参考 本次课程我们来学习下课程第八章——实战&#x…

基于传统机器学习模型算法的项目开发详细过程

1 场景分析 1.1 项目背景 描述开发项目模型的一系列情境和因素&#xff0c;包括问题、需求、机会、市场环境、竞争情况等 1.2. 解决问题 传统机器学习在解决实际问题中主要分为两类&#xff1a; 有监督学习&#xff1a;已知输入、输出之间的关系而进行的学习&#xff0c;从而…

技术阅读周刊第十四期:Golang 作者 Rob Pike 在 GopherConAU 上的分享

技术阅读周刊&#xff0c;每周更新。 历史更新 20231215&#xff1a;第十期20231122&#xff1a;第十一期20231129&#xff1a;第十二期20240105&#xff1a;第十三期&#xff1a;一些提高生产力的终端命令 What We Got Right, What We Got Wrong URL: https://commandcenter.b…

玩转 openEuler (一)-- 系统安装

简介 openEuler 是一款开源操作系统。当前 openEuler 内核源于Linux&#xff0c;支持鲲鹏及其它多种处理器&#xff0c;能够充分释放计算芯片的潜能&#xff0c;是由全球开源贡献者构建的高效、稳定、安全的开源操作系统&#xff0c;适用于数据库、大数据、云计算、人工智能等…

羊驼2:开放的基础和微调聊天模型--Llama 2论文阅读

论文地址&#xff1a;https://arxiv.org/pdf/2307.09288.pdfd 代码地址&#xff1a;GitHub - facebookresearch/llama-recipes: Examples and recipes for Llama 2 model 问答 用了多少个gpu&#xff1f; 这篇文档中使用了3.3M GPU小时的计算&#xff0c;使用的硬件类型是A…

3万字数据结构与算法学习笔记+知识点总结

文章目录 数据结构与算法排序排序算法常见排序算法复杂度冒泡排序&#xff08;Bubble Sort&#xff09;选择排序&#xff08;Selection Sort&#xff09;插入排序&#xff08;Insertion Sort&#xff09;希尔排序&#xff08;Shell Sort&#xff09;堆排序&#xff08;Heap Sor…

Microsoft Edge如何安装插件去广告

打开enge浏览器&#xff0c;点击这里 选择扩展 点击打开Microsoft Edge网站 点击搜索栏&#xff0c;输入广告拦截&#xff0c;之后点击键盘的Enter键 点击获取就可以了。如果你喜欢其他的插件&#xff0c;也可以在里面搜索并且下载

文献阅读:Large Language Models as Optimizers

文献阅读&#xff1a;Large Language Models as Optimizers 1. 文章简介2. 方法介绍 1. OPRO框架说明2. Demo验证 1. 线性回归问题2. 旅行推销员问题&#xff08;TSP问题&#xff09; 3. Prompt Optimizer 3. 实验考察 & 结论 1. 实验设置2. 基础实验结果 1. GSM8K2. BBH3.…

MyBatis的配置及简单使用

1.配置myBatis 1.myBatis的作用 MyBatis 是一个开源的持久层框架&#xff0c;它的主要作用是简化数据库操作&#xff0c;使得开发者能够更方便地与数据库进行交互。 MyBatis 允许开发者使用简单的 XML 或注解配置 SQL 映射&#xff0c;从而实现数据库操作&#xff0c;而不需要…

解决“win11无法识别U盘“问题

在15.6寸笔记本上插上U盘&#xff0c;有时候出现U盘无法识别的现象&#xff0c;出现这种问题的原因有许多&#xff0c;比如U盘的格式不被当前电脑支持、电脑的USB接口电压过低、没有安装U盘驱动等等。     若是U盘格式不支持&#xff0c;则把U盘改成电脑能够识别的格式&#…

C++I/O流——(4)文件输入/输出(第一节)

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 含泪播种的人一定能含笑收获&#xff…

Fiddler工具 — 13.AutoResponder应用场景

简单介绍几个应用场景&#xff1a; 场景一&#xff1a;生产环境的请求重定向到本地文件&#xff0c;验证结果。 例如&#xff1a;某网站或者系统修改了问题&#xff0c;但尚未更新到生产环境&#xff0c;可重定向到本地修改后的文件进行验证&#xff0c;这样能够避免更新到生产…

maven管理使用

maven基本使用 一、简介二、配置文件三、项目结构maven基本标签实践(例子) 四、pom插件配置五、热部署六、maven 外部手动加载jar打包方式Maven上传私服或者本地 一、简介 基于Ant 的构建工具,Ant 有的功能Maven 都有,额外添加了其他功能.本地仓库:计算机中一个文件夹,自己定义…

鸿蒙Harmony-层叠布局(Stack)详解

我们总是为了太多遥不可及的东西去拼命&#xff0c;却忘了人生真正的幸福不过是灯火阑珊处的温暖&#xff0c;柴米油盐的充实&#xff0c;人生无论你赚的钱&#xff0c;是多还是少&#xff0c;经历的事情是好还是坏&#xff0c;都不如过好当下的每一天&#xff01; 目录 一&am…