【C++ | 左值、右值】一文了解C++的左值、右值、左值引用()、右值引用()

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
⏰发布时间⏰:2024-06-12 16:35:55

本文未经允许,不得转发!!!

目录

  • 🎄一、概述
  • 🎄二、左值(lvalue)
  • 🎄三、右值(rvalue)
  • 🎄四、左值引用
  • 🎄五、右值引用(rvalue reference)
    • ✨5.1 右值引用的理解
    • ✨5.2 std::move
  • 🎄六、总结



在这里插入图片描述

🎄一、概述

左值、右值、左值引用、右值引用,这几个概念常常在C++编程中出现,如果没弄清楚这几个概念的意思,有时会在看一些C++书籍时会感到特别困惑。

左值和右值是表达式的属性。一些表达式生成或要求左值,而另外一些则生成或要求右值。一般而言,一个左值表达式表示的是一个对象的身份,而一个右值表达式表示的是对象的值。

左值、右值最初是出现在C语言中的,左值最初指的是可出现在赋值语句左边的实体,引入了const关键字之后,常规变量和 const 变量都可视为左值, 因为可通过地址访问它们,但常规变量属于可修改的左值, 而 const 变量属于不可修改的左值。右值一般是没有明确的内存位置,无法使用&获取地址,值不可被修改。例如:立即数、常量、字面值、&(变量)。

在C++中,增加了引用的概念,所以会出现左值引用、右值引用,下文将举例说明这几个概念。


在这里插入图片描述

🎄二、左值(lvalue)

左值:英文简写为“lvalue”,是“locator value”的缩写,可意为存储在内存中、有明确存储地址(可寻址)的数据。

左值:本质上是可以操作的一块内存区域,一般可以通过&取到该内存起始地址,这块内存的值可以被修改(const对象除外)。除了const对象的其他左值都可以出现在赋值语句左边。

常见的左值有:
1、变量(对象);
2、const 变量(对象);
3、对指针解引用,*(指针)
4、数组元素,a[1]
5、结构体成员、类成员,s.m_aps->ma

// g++ 13_lvalue.cpp
int main()
{
	int i = 0;			
	i = 1;				// i是变量(对象),左值
	
	const int ci = 5;	
	//ci = 6;			// ci是const变量(对象),是左值但不能出现在赋值号左边
	
	int *pi = &i;
	*pi = 1;			// *pi 对指针解引用,左值

	int arr[5] = {0,};
	arr[0] = 1;			// arr[0] 是数组元素,左值

	struct {
		int m_a;
		int m_b;
	} st, *pst=&st;
	st.m_a = 1;			// 结构体成员,左值
	pst->m_b = 2;

	return 0;
}

在这里插入图片描述

🎄三、右值(rvalue)

右值:英文简写为“rvalue”,是"read value"的缩写,指的是那些可以提供数据值的数据(不一定可以寻址,例如存储于寄存器中的数据)。

右值:指一种表达式,其结果是值而非值所在的位置。一般是没有明确的内存位置,无法使用&获取地址,值不可被修改的。例如:立即数、常量、字面值、&(变量)。另外,很多时候左值可以当右值使用。

关于右值的,有一个重要的原则,是在需要右值的地方 (除了移动构造函数) 可以用左值来代替,但是不能把右值当成左值(也就是位置)使用。当一个左值被当成右值使用时,实际使用的是它的内容(值)。

常见的右值有:
1、字面常量(立即数),引号括起的字符串除外(它们由其地址表示),42'a'
2、算术运算符(+、-、*、/、%、正号、负号)的求值结果,1+2
3、逻辑运算符(&&、||、!)的求值结果,a!=10
4、关系运算符(>、>=、<、<=、==、!=)的求值结果,a>10 && a<100
5、函数的非引用返回值。这种返回值位于临时内存单元中,运行到下一条语句时,它们可能不再存在;
6、当条件运算符的两个表达式都是左值或者能转换成同一种左值类型时,运算的结果是左值;否则运算的结果是右值。

// g++ 13_rvalue.cpp

int get100()
{
	return 100;
}

int main()
{
	int i = 0, c=0;			
	i = 42;				// 字面值42,右值
	c = 'a';			// 字面值'a',右值
	
	i = 1+2;			// 算术运算符的求值结果,右值
	
	i = (c!='a');		// 逻辑运算符的求值结果,右值
	
	i = (c >= 'a'); 	// 关系运算符的求值结果,右值
	
	i = get100();		// 函数的非引用返回值,右值
	
	i>0?i:c = i>0?1:0;	// 条件运算符的两个表达式都是左值,则可以做左值,本例的 i>0?i:c
						// 条件运算符的两个表达式都是右值,则为右值,本例的 i>0?1:0;
						
	i = c;				// c是左值,左值的值可以当右值使用

	return 0;
}

在这里插入图片描述

🎄四、左值引用

左值引用:左值引用这个词是在C++11增加了右值引用之后才出现,为了与右值引用区分开,把C++11之前出现的引用成为左值引用。所以,左值引用就是C++中的引用类型,是己定义的变量的别名,主要用作函数形参或返回值。

传统的 C++引用(现在称为左值引用) 使得标识符关联到左值,左值引用不能绑定到要求转换的表达式、字面常量或是返回右值的表达式。

const左值引用既可以绑定到左值,也可以绑定到右值。但不能修改const左值引用绑定的内容。

怎样声明、定义左值引用:左值引用的定义需要使用&,格式一般是类型 &变量名 = 变量。左值引用定义必须初始化,且初始化后无法改变该引用关联的对象。下面例子介绍了左值引用的定义、const左值引用初始化为左值、const左值引用初始化为右值,

// g++ 13_lvalue_reference.cpp
int get100()
{
	return 100;
}
int main()
{
	int i = 0;
	int &ri = i;		// 左值引用
//	int &*pri = &ri;	// 不能定义引用的指针。报错:cannot declare pointer to ‘int&’
//	int &&rri = ri;		// 不能定义引用的引用。在C++11标准里,这是一个右值引用
	
	int *pi = &i;
	int *&rpi = pi;		// pi是左值,rpi是指针的引用
	int &r_pi = *pi;	// *pi是左值,可以初始化左值引用
	
	int arr[5] = {0};
	int &rarr = arr[0];	// arr[0]是左值,可以初始化左值引用
	
	// const 左值引用可以被初始化为一些左值
	const int &cri = i;		// const 左值引用
//	const int *&crpi = pi;	// 报错:用 ‘int*’ 初始化 ‘const int*&’ 无效
	const int &cr_pi = *pi;	// const 左值引用
	const int &crarr= arr[0];
	
	// const 左值引用可以被初始化为右值
	const int &ri0 = 42;
	const int &ri1 = 'a';
	const int &ri2 = 1+2;
	const int &ri3 = (ri1 != 'a');
	const int &ri4 = (ri1 >= 'a');
	const int &ri5 = get100();		// 函数的非引用返回值,右值
	const int &ri6 = i>0?1:0;	
//	ri0 = 1;	// 报错,const引用是只读的,其值不能修改
	return 0;
}

左值引用常用作函数的形参、返回值。返回引用的函数是左值的,意味着这些函数返回的是对象本身而非对象的副本。返回左值引用的函数, 连同赋值、下标、解引用和前置递增/递减运算符, 都是返回左值的表达式的例子。

另外,应尽可能将引用形参声明为 const ,有以下三个理由:
• 使用 const 可以避免无意中修改数据的编程错误;
• 使用 const 使函数能够处理 const 和非 const 实参, 否则将只能接受非 const 数据;
• 使用 const 引用使函数能够正确生成并使用临时变量。

如果实参为右值, const 引用形参将指向一个临时变量。


在这里插入图片描述

🎄五、右值引用(rvalue reference)

右值引用:这是为了支持移动操作,C++11标准引入的一种新的引用类型。所谓右值引用就是必须绑定到右值的引用。我们通过 && 而不是 & 来获得右值引用。编译时,需要加 -std=c++11 来支持C++11标准。

类似任何引用,一个右值引用也不过是某个对象的另一个名字而已。

✨5.1 右值引用的理解

怎样定义右值引用?右值引用使用&&来定义,也是必须初始化,初始化后无法改变其绑定的对象。定义右值引用格式:类型 &&引用名称 = 右值;。定义一个右值引用可以参考下面代码:

int &&rl = 13;
int *pi = &rl;

将右值关联到右值引用导致该右值被存储到特定的位置,且可以获取该位置的地址。也就是说,虽然不能将运算符&用于 13,但可将其用于 rl。通过将数据与特定的地址关联,使得可以通过右值引用来访问该数据。

右值引用只能绑定到一个右值,右值要么是字面常量,要么是在表达式求值过程中创建的临时对象;

而临时对象有两个特点:
1、该对象将要被销毁;
2、该对象没有其他用户再使用它。
这就意味着,右值引用的代码是最后使用这个对象的了,可以自由地接管所引用的对象的资源。

在这里插入图片描述
如上图,变量a、b相加之后会产生一个值,这个值就是临时量,但它在内存中肯定是存在某个地址的,没有右值引用之前,这个值使用完就会被销毁,我们也不会知道它的内存地址。现在,这块内存可以被右值引用关联,关联后,右值引用甚至可以改变内存的内容,等右值引用使用完再销毁。

看看下面关于右值引用的例子,加深理解:

// g++ 13_rvalue_reference.cpp  -std=c++11
#include <iostream>
using namespace std;
int get100()
{
	return 100;
}

void fun(int &&rri)// 右值引用作为函数形参
{
	rri = 0;
}
int main()
{
	// 1、右值引用必须被初始化为右值
	int &&ri0 = 42;		// 将 42 存到一个临时量,然后引用这个临时量
	int &&ri1 = 'a';	// 将 'a' 存到一个临时量,然后引用这个临时量
	int &&ri2 = 1+2;	// 将 1+2 存到一个临时量,然后引用这个临时量
	int &&ri3 = (ri1 != 'a');
	int &&ri4 = (ri1 >= 'a');
	int &&ri5 = get100();	// 函数的非引用返回值,右值
	int &&ri6 = ri0>0?1:0;
	
	// 2、右值引用的内容可以被修改
	ri0 = 1;
	cout << "ri0=" << ri0 << endl;
	
	// 3、虽然没办法获取右值的地址,但可以获取右值引用的地址,并改变该地址的值
	int *pi = &ri0;
	*pi = 2;
	cout << "pi=" << pi << ", *pi=" << *pi << ", ri0=" << ri0 << endl;

	// 4、传入右值
	fun(1+2);
	return 0;
}

✨5.2 std::move

虽然不能将一个右值引用直接绑定到一个左值上, 但我们可以显式地将一个左值转换为对应的右值引用类型。通过调用 std::move 的标准库函数可以获取绑定到左值上的右值引用,此函数定义在 utility 头文件中。

int i = 0;
int &&rri = std::move(i);

std::move 告诉编译器,我们有一个左值,但希望像右值一样处理它。

注意,使用 std::move 函数处理的对象,被成为移后源对象,我们可以修改该对象的值,但不可以再使用这个对象的值了,虽然语法上允许,但可能产生一些不可知的结果。因为调用了std::move 函数处理一个对象,也应被视为即将销毁的对象,如果这个对象还需要在后面的代码使用,则不建议对它调用 std::move 函数处理。


在这里插入图片描述

🎄六、总结

👉本文介绍了C++的左值、右值、左值引用、右值引用。左值一般表示某一块内存,可以获取其地址;右值则一般只表示数据,不能被获取地址,很多情况,左值的值可以是右值;左值引用是某个左值的别名;右值引用是某个右值的别名。

在这里插入图片描述
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

鸿蒙轻内核A核源码分析系列五 虚实映射(5)虚实映射解除

虚实映射解除函数LOS_ArchMmuUnmap解除进程空间虚拟地址区间与物理地址区间的映射关系&#xff0c;其中参数包含MMU结构体、解除映射的虚拟地址和解除映射的数量count,数量的单位是内存页数。 ⑴处函数OsGetPte1用于获取指定虚拟地址对应的L1页表项数据。⑵处计算需要解除的无效…

python实现高斯(Gauss)迭代自动计算

实现高斯&#xff08;Gauss&#xff09;迭代自动计算 输入系数矩阵mx、值矩阵mr、迭代次数n&#xff0c;即可得到答案。本人在原博主的代码基础上优化了数据输出形式&#xff0c;原文链接&#xff1a;python实现高斯(Gauss)迭代法_python中gausspp-CSDN博客 运算结果如下图&am…

家用洗地机怎么选?四大行业精品集合,识别度超高

家用洗地机&#xff0c;作为一种能够高效清洁地面的清洁工具&#xff0c;不仅减轻了人们家务的轻度&#xff0c;也给人们腾出了很多空闲的时间去享受生活。但是洗地机那么多&#xff0c;我们在面对洗地机选购的时候&#xff0c;我们应该要注意哪些呢&#xff1f;下面就为大家详…

游戏研发(策略+sass+回调模式)

前言 由于这边需要对接游戏研发后台,基本就是开服,封禁.角色日志等,但是每个游戏提供的接口都是不一样的,所以为了统一处理提前进行sass封装,以便后续可以更好的兼容 同时还涉及了多数据源的问题,因为有些日志太大不可能直接去http调用,会使用直接查询游戏研发的数据库方式这一…

从零实现KV存储项目实战

本项目是从零实现一个完整的、兼容Redis协议的KV数据库项目。 通过每一行代码的编写。你会对整个系统了如指拿&#xff0c;这样对自己基本功的锻炼、对编程能力的提升都是很大的 项目提供完整的视频教程代码 下面是关于KV存储项目的技术大纲&#xff1a; 如果你在学习的过程…

BUAA-2024年春-OO第四单元总结

正向建模与开发 在本单元中&#xff0c;我们需要模拟一个小型的图书管理系统&#xff0c;完成图书馆所支持的相关业务&#xff0c;并遵守一定的规章制度。与前几次不同的是&#xff0c;本单元中&#xff0c;我们需要预先将自己的设计思路用UML来实现&#xff0c;然后进行编程。…

Ecovadis审核的内容

Ecovadis审核的内容。Ecovadis是一家国际性的企业社会责任评估机构&#xff0c;旨在为全球供应链的可持续性发展提供评估和审核。在本文中&#xff0c;我们将从以下几个方面详细介绍Ecovadis审核的内容&#xff1a; 一、Ecovadis审核的范围和目的 Ecovadis审核的范围涵盖了各个…

EMI电路

PFC 功率部分 1 、整流桥是串联 2 、 PFC 电感串联 3 、二极管并联 4 、 MOSFET 并联 EMI电路图

C++中的结构体——结构体嵌套结构体

作用&#xff1a;结构体中的成员可以是另一个结构体 例如&#xff1a;每一个老师辅导一个学生&#xff0c;每个老师的结构体中&#xff0c;记录一个学生的结构体 示例 运行结果

springboot二屯村钓鱼场管理系统-计算机毕业设计源码58167

摘 要 在互联网时代的来临&#xff0c;电子商务的骤起&#xff0c;一时间网络进行购物这一形式备受欢迎&#xff0c;到现在&#xff0c;网购更是普及。现如今各个行业也通过网购的方式来进行拓展业务&#xff0c;增加企业的知名度以及提升业绩&#xff0c;满足了用户像网购一样…

云原生应用开发培训,开启云计算时代的新征程

在云计算时代&#xff0c;云原生应用开发技术已经成为IT领域的热门话题。如果您想要转型至云原生领域&#xff0c;我们的云原生应用开发培训将帮助您开启新征程。 我们的课程内容涵盖了云原生技术的基础概念、容器技术、微服务架构、持续集成与持续发布&#xff08;CI/CD&#…

单细胞RNA测序(scRNA-seq) 理解Seurat对象存储信息含义和基本操作

单细胞测序技术是在单个细胞水平上&#xff0c;对基因组、转录组和表观基因组水平进行分析测序技术。bulk RNA-seq获得的是组织或器官等大量细胞中表达信号的均值&#xff0c;无法获取细胞之间的差异信息&#xff08;即丢失了细胞的异质性&#xff09;&#xff0c; 而单细胞测序…

【数组】【双指针】三数之和

打算冲一把算法类比赛&#xff0c;之前一直对算法提不起兴趣&#xff0c;也有我自己对它的抵触&#xff0c;本身算法也比较菜。 但现在打算勤勤恳恳刷题&#xff0c;踏踏实实总结&#xff0c;冲&#xff01; 数组——双指针 三数之和 该题力扣网址 错误做法 三重循环框架&a…

第十五篇——条件熵和信息增益:你提供的信息到底值多少钱?

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么&#xff1f; 四、总结五、升华 一、背景介绍 通过这篇文章&#xff0c;我知道了条件熵和信息增益&#xff1b;如果你试…

水电站大坝安全监测工作详解

水电站大坝安全监测是确保大坝结构安全和操作安全的关键组成部分。本文将详细解释水电站大坝安全监测的9项主要工作内容&#xff0c;帮助理解其重要性和执行过程。 1) 现场监测 现场监测是水电站大坝安全监测的首要步骤。监测人员需要定期对大坝的物理结构进行检查&#xff0c;…

vite构建的ts项目配置src别名@

一、安装types/node npm install types/node 二、vite.config.ts 文件中配置以下内容 resolve: {alias: {: path.resolve(__dirname, ./src),},}, 三、 tsconfig.json 文件中compilerOptions下配置以下内容 /* 配置 */"baseUrl": ".","paths":…

【Python】详解pandas库中pd.merge函数与代码示例

&#x1f60e; 作者介绍&#xff1a;我是程序员洲洲&#xff0c;一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主。 &#x1f913; 同时欢迎大家关注其他专栏&#xff0c;我将分享Web前后端开发、人工智能、机器学习、深…

高考志愿填报秘籍:个人篇

选择适合自己的大学和专业&#xff0c;对广大考生来说至关重要。从某种程度上来说&#xff0c;决定了考生未来所从事的行业和发展前景。为了帮助广大考生更加科学、合理地填报志愿&#xff0c;选择适合自己的大学和专业&#xff0c;本公众号将推出如何用AI填报高考志愿专栏文章…

清远mes系统开发商 盈致科技

清远MES系统开发商盈致科技为企业提供专业的MES系统解决方案&#xff0c;帮助企业实现生产过程的数字化管理和优化。盈致科技的服务范围包括但不限于以下方面&#xff1a;MES系统定制开发&#xff1a;盈致科技可以根据清远企业的实际需求定制开发适合的MES系统&#xff0c;满足…