C++引用篇

news2024/9/20 6:14:00

文章目录

  • 一、引用概念及示例
  • 二、引用做函数参数
  • 二、引用做函数的返回值
  • 四、常引用
  • 五、引用和指针的区别


一、引用概念及示例

c语言指针存变量地址,然后通过解引用可以访问或者改变变量,且也可以改变指针变量里面存的地址
在这里插入图片描述

修改变量这样还需要对指针变量解引用,这样较为麻烦,且修改地址还有些不安全,所以c++中有了同样能修改变量且可以访问变量的名词
这被成为引用引用 引用:引用不是新定义的变量,而是给已存在的变量取一个别名,
不过编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间
在这里插入图片描述
在这里插入图片描述

引用操作符&它这与取地址操作符一样,不过引用在使用时引用是双目,&左边数据类型,右边为引用变量名,
在这里插入图片描述

而取地址操作符是单目操作符,右边为取地址变量名
在这里插入图片描述

引用就相当于给别人起外号,虽然一个人可能有很多外号,但是这些外号对应的还是那个人 而对其中一个外号进行改动,其它外号都是在变化的
如有一个人叫张三,别人又叫他法外狂徒,如今我们将法外狂徒判刑十年,是不是张三被判刑了10年

虽然一个变量可能有不同的别名但是这些别名它的空间都为同一个空间,其中一个别名改变 也会影响到其它的别名甚至他本身。
在这里插入图片描述

#include<iostream>

//在引用时要初始化,要说明是谁的引用
using namespace std;
int main()
{
	int a = 0;
	int& b = a;//对a起别名
	int& c = b;//对b起别名

	c++;//b的别名改变
	cout << a << endl;
	cout << b << endl;
	cout << c << endl;

	b++;//a的别名改变
	cout << a << endl;
	cout << b << endl;
	cout << c << endl;

	//b是a的引用,c是b的引用,它们和它所引用的对象共用同一块空间,所以它们地址都相同
	cout << &a << endl;
	cout << &b << endl;
	cout << &c << endl;

	return 0;
}

在这里插入图片描述

二、引用做函数参数

引用作为函数参数的形参,在函数调用时,相当于形参是对实参的引用,所以在函数内部对实参的操作也就是对实参的操作。引用做参数,形参和实参共同有一块内存空间,没有为形参开辟新的内存空间,减少了调用时因为形参开辟内存的损耗,提高了效率

#include<iostream>
using namespace std;
//交换函数
//形参x是实参a的引用
//形参y是实参b的引用
void Swap(int& x, int& y)
{
    //交换变量x和y的值就是交换变量a和b的值
	int temp = x;
	x = y;
	y = temp;
	//x与a的地址相同
	cout <<"形参x地址:"<< &x << endl;
	//y与b的地址相同
	cout <<"形参y地址:" << &y << endl;
	//编译器并没有为它们开辟临时空间

int main()
{
	int a = 10;
	int b = 20;
	cout << "交换前:"<<endl;
	cout << "a:"<<a << endl;
	cout <<"b:"<< b << endl;

	Swap(a, b);
	cout << "交换后:"<<endl;
	cout <<"a:"<< a << endl;
	cout <<"b:"<< b << endl;

	return 0;
}

在这里插入图片描述

而不是引用做参数的,要达到交换函数的话,在调用函数时,就需要传实参地址,形参为指针,且还要为这形参(指针变量)开辟临时空间。

//传址
void Swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
	cout <<"形参x地址:"<< &x << endl;//x与a的地址并不相同,即使x指向a的地址,但是只要它创建了一个变量,那么编译器就会为起分配内存空间
	cout <<"形参y地址:"<< &y << endl;//y与b的地址并不相同
}

//传值
int add(int a, int b)
{
	cout <<"形参a地址:"<< &a << endl;
	cout <<"形参b地址:"<< &b << endl;
	return a + b;
}

int main()
{
	int a = 10;
	int b = 20;
	cout << "交换前:" << endl;
	cout << "a:" << a << endl;
	cout << "b:" << b << endl;

	cout <<"实参a地址:"<< &a << endl;
	cout <<"实参b地址:"<< &b << endl;
	Swap(&a, &b);
	cout << "交换后:" << endl;
	cout << "a:" << a << endl;
	cout << "b:" << b << endl;

	int x = 1;
	int y = 2;
	cout <<"实参x地址:"<< &x << endl;
	cout <<"实参y地址:"<< &b << endl;

	int ret = add(x, y);
	cout << ret << endl;
	
	return 0;
}

在这里插入图片描述

不是引用型做参数的,在函数调用时实参无论是传值还是传址,形参变量在定义时编译器都会为其开辟一个临时内存空间,形参都有自己的地址,在调用时会为形参开临时空间有一定的损耗,降低效率,引用做参数时,没有为形参开临时空间,减少了因开临时空间的损耗,提高了效率。当参数是一个很大时,效率比引用做参数低很多

以形参为一个很大的结构体为例

struct A
{
	int a[100000];
};

void f(A c)//c++可以直接用结构体名
{

}

//引用没有开临时空间,没有那些消耗
void f1(A&c){}

void TestValue()
{
	A a;
	size_t begin1 = clock();
	for (int i = 0; i < 10000; i++)
	{
		f(a);
	}
	size_t end1 = clock();

	size_t begin2 = clock();
	for (int i = 0; i < 10000; i++)
	{
		f1(a);
	}
	size_t end2 = clock();

	cout << "f_time:" << end1 - begin1 << endl;
	cout << "f1_time:" << end2 - begin2 << endl;
}

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


在这里插入图片描述

引用做参数极大的提高了效率

二、引用做函数的返回值

值返回和引用返回,引用返回时,返回的对象具有左值特性,可以被修改

值返回

int fun1()
{
	int n = 0;
	n++;
	return n;
}

int main()
{
	int ret = fun1();
	cout << ret << endl;
	return 0;
}

在这里插入图片描述

n并不是直接返回给ret,它中间会生成一个临时变量,一般是寄存器,然后作为返回值给ret,但是不一定是寄存器,寄存器只有四个字节或者8个字节,数据量大就放不下,因此有时候生成的临时变量不一定是寄存器
返回值拷贝给临时变量,临时变量拷贝给接收变量,两次拷贝

在这里插入图片描述

为什么会生成临时变量空间?

函数调用为函数开辟空间,在main函数中为变量ret申请了一块空间,存放ret的值,main函数调用了fun1函数,fun1函数中为n申请了一块空间。当fun1函数调用结束时,fun1空间销毁,但是需要将fun1函数中的n的值返回,n在局部域里面,不能用n作为返回值,fun1函数都已经调用结束如何能返回,通过将n的值放入寄存器中,通过寄存器将值带回给调用它的那个函数,而将值带回这个过程生成了临时变量,然后将值拷贝给这个临时变量,将返回值带回,同样有因开辟临时空间带来的消耗。

引用返回
如何避免因生成临时变量带来的消耗?

引用返回,不生成临时变量减少了拷贝带来的消耗,提高了效率,小对象影响不大,但是大对象时就极大的提高了效率
在这里插入图片描述

当大对象做返回时,引用和值返回效率有很大差异

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

int main()
{
	TestReturnByRefOrValue();

	return 0;
}

在这里插入图片描述

引用做返回能极大的提高效率

但是不是每个场景都能用引用返回
在这里插入图片描述

fun1函数结束,栈帧销毁,如果没有清理栈帧,那么ret的结果就会是正确的,
fun1函数结束,栈帧销毁,如果清理栈帧,那么ret的结果就会是随机值,他这块栈帧空间里面的内容都被清理了,此时再访问时就会出现随机值。

int& fun()
{
	//static int n = 0;
	int n = 0;
	n++;

	cout << &n << endl;
	return n;
}

int main()
{
	
	const int& ret = fun();//由于这个常量返回是通过寄存器保存,然后回来时再将寄存器里面的值赋值给ret,
	cout << ret << endl;
	cout << &ret << endl;
	
	return 0;
}

在这里插入图片描述
它两个地址是相同的
在这里插入图片描述

fun函数栈帧没有清理,编译器一般是不会清理栈帧空间的内容,但是这个空间里的内容有可能为随机值,将这块空间重新赋值。
在这里插入图片描述

ret是n的别名,再调用fun函数,也就将上一个函数的栈帧空间覆盖了给新调用的fun分配栈帧空间,此时再访问ret,ret的值可能为随机值,访问销毁空间上的变量,像数组那样越界访问了。
函数栈帧没有清理,再调用另外一个函数,而这个函数又要建立栈帧,初始化,然后这个栈帧将上一个函数的栈帧空间覆盖了,此时再访问ret,就会是随机值
在这里插入图片描述
后面栈帧覆盖前一个栈帧,此时再访问就是一个随机值

用引用返回出了函数作用域再访问这样的操作就相当于野指针访问十分危险。所以要返回的值只能作用在该函数中,不可用引用返回,当函数栈帧销毁不会影响返回的值,可以用引用返回的,引用返回适用于全局变量,静态变量(static),由malloc出来的
返回值出了作用域引用对象不在的不适用引用返回。

对顺序表赋某个位置的值赋值+修改
将pos位置值改为10,一般的话需要获得pos位置的值,然后再对这个位置的值修改为10再将10赋值给顺序表pos位置赋值

#include<assert.h>
struct SeqList
{
	int a[100];
	size_t size;
};

//将pos位置的值修改为x
void SLModify(SeqList* ps, int pos, int x)
{
	assert(pos >= 0 && pos < 100);
	ps->a[pos] = x;
}

//获取pos位置的值
int Get(SeqList* ps, int pos)
{
	assert(pos >= 0 && pos < 100);
	return ps->a[pos];
}

int main()
{
	SeqList s;
	SLModify(&s, 0, 1);

	int ret = Get(&s, 0);
	cout << ret << endl;
	ret += 5;//只是得到pos位置的值修改并没有做到对顺序表进行修改
	cout << ret << endl;
	SLModify(&s, 0, ret);//要修改还需要重新调用修改顺序表的接口
}

在这里插入图片描述

这样很麻烦,明明都将值修改了结果还要再调用修改顺序表接口函数,因此用引用返回可以做到获取pos位置值时就将其修改

而引用做函数的返回值,它返回的对象具有左值的特性,可以被修改,一般函数的返回值对象是不可被修改的,所以可以直接获取pos位置的值的别名,然后对其修改其实也就是对顺序表修改

#include<assert.h>
struct SeqList
{
	int a[100];
	size_t size;
};


void SLModify(SeqList* ps, int pos, int x)
{
	assert(pos >= 0 && pos < 100);
	ps->a[pos] = x;
}

int& SLAt(SeqList* ps, int pos)
{
	assert(pos >= 0 && pos < 100);
	return ps->a[pos];
}

int main()
{
	SeqList s;
	SLModify(&s, 0, 1);

	SLAt(&s, 0) = 10;//引用返回的具有左值特性,可修改
	cout << SLAt(&s, 0) << endl;
	cout << SLAt(&s, 0)+5 << endl;
	return 0;
}

在这里插入图片描述

引用做返回值

  1. 减少拷贝提高效率
  2. 修改返回值和获取返回值

四、常引用

在定义引用变量时用const修饰,被定义的引用变量也就是常引用
一般引用
在这里插入图片描述
在这里插入图片描述
a可修改可读,可以对a引用,b对a的引用过程中为权限的平移

在这里插入图片描述

这里引用时,a变量被const修饰不可修改,只读,而如果对其引用就会变为可修改,修改了它的权限,是错误的。在引用过程中权限不能放大
那么如何将其变为正确的,在

在这里插入图片描述

加const修饰,成为常引用

在这里插入图片描述

在这里插入图片描述

z为x的别名,但是现在它只有可读属性,不可被修改,原本变量的可改属性没有了,所以在引用过程中,权限可以缩小。以z访问时,不可以修改它也就不可以修改x,但是以x访问时,x改变,此时z也会改变,z和x共用空间,只是不能通过z改变,但可以通过x改变z
在这里插入图片描述
在这里插入图片描述
为何不可?
由于
在这里插入图片描述
不同类型转换才会产生临时变量,临时变量为右值,具有常性,相当于加了const修饰,不可被修改,因此可以常引用
在这里插入图片描述

在这里插入图片描述

引用时权限不可放大,需要加const修饰使其成为常引用

在这里插入图片描述

在这里插入图片描述

这是可以的,权限平移

常引用
在这里插入图片描述

五、引用和指针的区别

引用在定义时必须初始化,指针没有要求
引用是已知变量的别名,指针是地址
引用在引用一个实体后不能再引用其它实体,但是指针可以指向其它实体
没有NULL引用,但是有NULL指针
计算大小字节时,引用的大小为引用类型的大小,指针始终时地址空间所占字节个数4或者8字节
引用自增就是引用的实体加一,而指针自增是向后偏移一个类型的大小
引用比指针使用起来更安全(指针空指针,野指针)
语法上引用没有开空间,指针开空间

在这里插入图片描述

但是从底层汇编指令角度看,引用类似指针的方式实现的
指针汇编
在这里插入图片描述
引用汇编
在这里插入图片描述

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

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

相关文章

Faster RCNN系列3——RPN的真值详解与损失值计算

Faster RCNN系列&#xff1a; Faster RCNN系列1——Anchor生成过程 Faster RCNN系列2——RPN的真值与预测值概述 Faster RCNN系列3——RPN的真值详解与损失值计算 Faster RCNN系列4——生成Proposal与RoI Faster RCNN系列5——RoI Pooling与全连接层 目录 一、RPN真值详解二、…

手把手教你实现el-table实现跨表格禁用选项,以及禁用选择后,对应的全选按钮也要禁用任何操作

哈喽 大家好啊 今天我要实现不能跨表格选择&#xff0c;如果我选择了其中一个表格的选项后&#xff0c;那么其他的表格选项则被禁用 然后我选择了其中一个表格行&#xff0c;我其他的表格选项则应该被禁用 实现代码&#xff1a; 其中关键属性&#xff1a; selectable仅对 typ…

如何保障企业网络安全

随着信息技术的迅速发展&#xff0c;网络已经渗透到了我们生活的方方面面。企业对网络的依赖程度也越来越高&#xff0c;网络安全问题已经成为了企业面临的一个重要挑战。那么&#xff0c;在这个风险重重的网络世界里&#xff0c;我们如何充分利用现有技术保障企业网络安全呢&a…

智能指针——C++

智能指针相较于普通指针的区别&#xff0c;就是智能指针可以不用主动释放内存空间&#xff0c;系统会自动释放&#xff0c;避免了内存泄漏。 1、unique_ptr&#xff1a;独占指针 需包含的头文件&#xff1a;#include <memory> unique_ptr 三种定义方式 先定义一个类 …

learn_C_deep_5 (温故知新、sigend char a = -128的深度理解、unsigned int类型的写法规范)

目录 温故知新 理解"unsigned int a -10;" 如何理解大小端 大小端的概念 大小端是如何影响数据存储的 sigend char a -128的深度理解 10000000为什么是-128&#xff0c;而不是-0 代码练习 unsigned int类型的写法规范 温故知新 理解"unsigned int a…

python数据结构与算法-动态规划(最长公共子序列)

一、最长公共子序列问题 1、问题概念 一个序列的子序列是在该序列中删去若干元素后得 到的序列。 例如&#xff1a;"ABCD”和“BDF”都是“ABCDEFG”的子序列。 最长公共子序列(LCS) 问题: 给定两个序列X和Y&#xff0c;求X和Y长度最大的公共子字列。 例:X"ABBCBDE”…

【ABAQUS Python二次开发】 debug : ini解析ERROR:没有实例属性‘__getintem__’

我的主页&#xff1a; 技术邻&#xff1a;小铭的ABAQUS学习的技术邻主页博客园 : HF_SO4的主页哔哩哔哩&#xff1a;小铭的ABAQUS学习的个人空间csdn&#xff1a;qgm1702 博客园文章链接&#xff1a; https://www.cnblogs.com/aksoam/p/17287136.html abaqus python 搭配ini…

古埃及:金字塔

文章目录 I 建造金字塔1.1 切割巨石1.2 开凿巨石1.3 摞石1.4 大金字塔的入口呈三角形 see also I 建造金字塔 在生活中&#xff0c;事实是正确的&#xff0c;如果理论解释不了现实&#xff0c;需要更正理论。 1.1 切割巨石 建筑材料巨石的切割&#xff1a;把石英砂粘在了铜锯…

记一次Macbook pro电池修复

记一次Macbook pro电池修复 mac版本 A1708 问题描述 Macbook更换新电池后&#xff0c;在项头栏中&#xff0c;没有显示电池图标&#xff0c;系统设置里面也找不到电池图标。这样开机还得连着电源线 ~ ^~ 原因分析&#xff1a; 有可能是电池排线坏了。 解决方案&#xff1a…

【C/C++】C++11 线程库重大历史意义

文章目录 C11 线程库重大意义【C11 中最重要的特性&#xff1a;就是对线程进行支持】API 比较C11 线程库APILinux/Win 系统线程库 API代码示例 Demo C11 线程库重大意义【C11 中最重要的特性&#xff1a;就是对线程进行支持】 C11 线程库解决了历史多线程跨平台问题&#xff0…

C++语法(20)---- 模拟红黑树

C语法&#xff08;19&#xff09;---- 模拟AVL树_哈里沃克的博客-CSDN博客https://blog.csdn.net/m0_63488627/article/details/130229501?spm1001.2014.3001.5501 目录 1.红黑树介绍 2.模拟实现 1.枚举红黑颜色 2.节点的定义 3.树类框架 4.插入 5.检查 3.代码实现 1…

【开发经验】spring事件监听机制关心的同步、异步、事务问题

文章目录 spring发布订阅示例同步核心源码分析如何配置异步事务问题 观察者模式又称为发布订阅模式&#xff0c;定义为&#xff1a;对象间的一种一对多的依赖关系&#xff0c;当一个对象的状态发生改变时&#xff0c;所有依赖它的对象都得到通知并被自动更新。 如下图所示&…

【Go】六、并发编程

文章目录 并发编程1、并发介绍2、Goroutine3、runtime包 3、Channel3.1、channel相关信息 4、Goroutine池&#xff08;❌&#xff09;5、定时器6、select多路复用7、并发安全和锁8、Sync9、原子操作&#xff08;atomic包&#xff09; 并发编程 1、并发介绍 1、进程和线程 ​…

心塞,被面试官在朋友圈吐槽了

​前阵子一个后辈小学弟向我诉苦&#xff0c;说自己在参加某大厂测试的时候被面试官怼得哑口无言&#xff0c;场面让他一度十分尴尬。 印象最深的就是下面几个问题&#xff1a; 自动化测试中&#xff0c;如何解决Case依赖&#xff1f;你们公司业务中&#xff0c;自动化和手工分…

“五一”预订量创5年新高!如何制定营销活动引爆门店客流?

作为疫情3年经济复苏后&#xff0c;2023年的第一个长假&#xff0c;今年“五一”的消费需求将全面集中释放&#xff0c;带动全国各地线下实体生意全面复苏。 根据官方平台发布的数据显示&#xff0c;今年五一的旅游订单比疫情前的2019年增长了200%&#xff0c;是近5年预订量最多…

Spring Security 整体架构

Spring Security 整体架构 整体架构 在的架构设计中&#xff0c;认证 和 授权 是分开的&#xff0c;无论使用什么样的认证方式。都不会影响授权&#xff0c;这是两个独立的存在&#xff0c;这种独立带来的好处之一&#xff0c;就是可以非常方便地整合一些外部的解决方案。 认…

(数字图像处理MATLAB+Python)第五章图像增强-第三节:基于照度反射模型的图像增强

文章目录 一&#xff1a;基于同态滤波的增强&#xff08;1&#xff09;概述&#xff08;2&#xff09;程序 二&#xff1a;Retinex理论&#xff08;1&#xff09;Retinex理论概述&#xff08;1&#xff09;SSR&#xff08;单尺度Retinex 算法&#xff09;&#xff08;2&#xf…

Oracle的学习心得和知识总结(二十二)|Oracle数据库Real Application Testing之Database Replay实操(二)

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《Oracle Database SQL Language Reference》 2、参考书籍&#xff1a;《PostgreSQL中文手册》 3、EDB Postgres Advanced Server User Gui…

操作系统概述及Linux基本指令(1)

目录 一. 操作系统 1.1 什么是操作系统 1.2 操作系统的核心工作 二. Linux的基本指令 2.1 ls指令 -- 打印文件名 2.2 pwd指令 -- 显示路径 2.3 cd指令 -- 进入特定目录 2.4 touch指令 -- 创建普通文件 2.5 mkdir指令 -- 创建路径 2.6 rmdir/rm指令 -- 删除路径或普通…

【GeoDjango框架解析】空间方法的ORM查询

原文作者&#xff1a;我辈理想 版权声明&#xff1a;文章原创&#xff0c;转载时请务必加上原文超链接、作者信息和本声明。 Django数据操作-ORM 第一章 【Django开发入门】ORM的增删改查和批量操作 第二章 【Django开发入门】ORM查询分页以及返回Json格式 文章目录 Django数据…