C++——左值和右值的本质区别

左值和右值好干嘛?

深入理解左值和右值可以帮助我们对代码进行优化

一、什么是左值和右值

左值:有某种存储支持的变量
右值:临时值(字面量、函数的结果)

Ⅰ右值是字面量

int yy = 22;

22本身就是一个临时的,系统不会存储它,故22是右值
而yy是一个int型变量,系统会为其开辟一个int大小的内存空间,故yy是左值

这下就应该很容易理解了吧

右值可以赋值给左值,左值不可以赋值给右值

int yy = 99;//右值99可以赋值给左值yy
10 = yy;//左值yy不可以赋值给右值10

其本质是因为左值是有地址的,有存储空间;而右值没有,是一个临时值而已,存活时间不长

这时候就可以优化代码了,我们不需要过于关注右值,因为它活不长,这就涉及到优化的问题了

可以证明字面量是个右值!

Ⅱ右值是函数的结果

①函数无参数

beyond是一个返回一个int的函数,其是一个右值

int year = beyond();year是左值,函数的结果是个右值,右值可以赋值给左值,正确
beyond() = year;报错,因为beyond()是右值,而year是左值,左值不可以赋值给右值

int beyond() 
{return 1993;
}int main() 
{int year = beyond();beyond() = year;//error,表达式必须是可修改的左值return 0;
}

可以证明函数的结果是右值!

②函数有参数

函数beyond接收的参数为左值,因为year是一个int型,有存储地址

beyond(1993);若调用函数beyond,传入右值也是可以的
这种情况,当函数被调用时,会自动通过该右值1993来创建一个左值,例如:int temp = 1993; beyond(temp);

int beyond(int year) 
{return year;
}int main() 
{int year =1993;beyond(year);beyond(1993);//1993为临时变量右值,它会被系统自动转为一个左值使用return 0;
}

二、左值引用

Ⅰ函数返回类型为左值引用

函数beyond返回值为左值引用(int&),也就是return year;这个左值的引用
这下子,函数的结果就变成了左值,其值就是左值year
int year = beyond();左值引用beyond可以赋值给左值
beyond() = 1999;因为beyond()时左值,故可以将右值1999赋值给左值beyond()

#include <iostream>int& beyond()
{int year = 1993;std::cout << "beyond_year: " << year << std::endl;return year;
}int main()
{int year = beyond();//√beyond() = 1999;//√return 0;
}

Ⅱ函数参数为左值引用

函数beyond的参数为左值引用(int& year),int year是个左值,取它的引用

void beyond(int& year){}int main()
{int year = 1993;beyond(year);//√beyond(1993);//× 非常量引用的初始值必须为左值return 0;
}

可以看到,函数参数为左值引用,若传入一个右值会报错!
变通方法:加上const即可
这也是很多项目里面函数参数常使用const的原因之一,因为函数参数可以传入左值和右值

const int& year加上const就可以了,既可以传入左值,也可以传入右值

void beyond(const int& year){}int main()
{int year = 1993;beyond(year);//√beyond(1993);//√return 0;
}

const+左值引用的本质

右值10是不可以赋值给左值引用int &a的

int& a = 10;//×

若加上const就可以了,因为有编译器默默的进行了转换,将右值10转换为一个临时变量temp,然后把这个临时变量赋值给左值

const int& a = 10;//√//等价于
int temp = 10;
int &a = temp;

const int&可以接收左值和右值,建议使用

Ⅲ如何判断是左值还是右值

写个函数,函数的参数是左值引用即可,因为右值的话传不进来
vocal、guitarist 、band 是左值
"Huangjiaju"、"Huangguanzhong"和vocal + guitarist是右值
其中vocal + guitarist组成了一个临时字符串,然后赋值给左值band
临时值也就是右值,没有地址

#include <iostream>void beyond(std::string& name)
{std::cout << name << std::endl;
}int main()
{std::string vocal = "Huangjiaju";std::string guitarist = "Huangguanzhong";std::string band = vocal + guitarist;beyond(vocal);beyond(guitarist);beyond(band);beyond(vocal + guitarist);//×return 0;
}

常量引用(如const int&)能兼容临时的右值和实际存在的左值变量

三、右值引用

左值引用(例如:const int&)接收左值,类似的,右值引用接收右值(临时对象)

右值引用,例如:const int&&,相对于左值引用多了一个&

将函数beyond的参数改为右值引用std::string&& name,报错的就是传入左值了

#include <iostream>void beyond(std::string&& name)
{std::cout << name << std::endl;
}int main()
{std::string vocal = "Huangjiaju";std::string guitarist = "Huangguanzhong";std::string band = vocal + guitarist;beyond(vocal);//×beyond(guitarist);//×beyond(band);//×beyond(vocal + guitarist);//√return 0;
}

此时,肯定会有同学发现,若要进行函数重载,一个是const+左值引用;另一个是右值引用会咋样?
const + 左值引用支持左值和右值(const std::string& name
右值引用只能传入右值(std::string&& name

#include <iostream>void beyond(const std::string& name)
{std::cout << "const + 左值引用" << name << std::endl;
}void beyond(std::string&& name)
{std::cout << "右值引用" << name << std::endl;
}int main()
{std::string vocal = "Huangjiaju";std::string guitarist = "Huangguanzhong";std::string band = vocal + guitarist;beyond(vocal);//√beyond(guitarist);//√beyond(band);//√beyond(vocal + guitarist);//√return 0;
}

贴下结果
在这里插入图片描述
分析:可以看到beyond(vocal + guitarist);调用的是右值引用的函数
vocal + guitarist是右值,std::string&& name只能接收右值,const std::string& name既可以接收左值,也可以接收右值
编译器最终选择了std::string&& name
这种设计的主要目的是允许开发者提供特定于右值的优化,如移动语义,从而提高代码的性能

四、小结

左值是有实际内存空间的,右值是临时值
左值引用只接受左值,右值引用只接受右值
左值引用加上const既可以接收左值也可以接收右值

会不会有同学问:为啥没有右值引用加const呢?
是不是小傻蛋?右值就是一个临时值,很快就会消失的,哪来的const修饰?

看了很多博客,解释各有差异,也有很多坑
这是我理解透之后总结出来的,感谢网上的很多博主的博客,看了很多,这里就不一一列举了

如果该篇博文对您产生了一丢丢的帮助,还请各位观众老爷给个赞,谢谢~~

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

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

相关文章

centos查看硬盘资源使用情况命令大全

在 CentOS 系统中&#xff0c;你可以使用几个命令来查看硬盘的资源和使用情况。以下是一些常用的命令&#xff1a; 1. df 命令 df (disk free) 用于显示文件系统的磁盘空间占用情况。 df -h-h 参数表示以人类可读的格式&#xff08;如 GB, MB&#xff09;显示。输出会显示每…

【爬虫分享】

爬虫分享 1、爬虫科普 视频发送于2024-10-27 14 _50.mp4 全屏预览下载附件 所以 爬虫 其实是非常 可“刑” 可“铐” 的。 2、逆向方法 算法还原 补环境 无头浏览器&#xff08;自动化&#xff09; rpc 参数生成速度&#xff1a;算法还原 > 补环境 > rpc > 无头…

【iOS】知乎日报第三周总结

【iOS】知乎日报第三周总结 文章目录 【iOS】知乎日报第三周总结前言评论区文字评论区的一个展开效果评论区数据的一个请求修改了主页获取数据的逻辑主页无限轮播图图片主色调的一个获取将一些拓展部分的内容写在分类里小结 前言 本周笔者因为金工实习整个项目进展比较慢&#…

OpenAI的Triton能替代Nvidia的CUDA吗

先说我的观点&#xff0c;我觉得可以&#xff0c;但是应该不是现在。 然后得补个概念&#xff0c;啥是Triton OpenAI的Triton 是一种专为高效编写深度学习运算而设计的编程语言和编译器。它旨在简化用户编写针对现代GPU&#xff08;尤其是NVIDIA GPU&#xff09;的自定义运算…

【黑马Redis原理篇】Redis数据结构

视频来源&#xff1a;原理篇[2,15] 文章目录 1.动态字符串SDS1.1 内部结构&#xff1a; 2.IntSet3.Dict3.1 dict的内部结构3.2 dict的扩容 4.ziplist压缩列表5.QuickList6.SkipList跳表7.RedisObject对象8.Redis的五种数据结构8.1 String8.2 List8.3 Set8.4 Zset 有序集合8.5 …

SpringBoot 创建多模块项目 项目分模块 项目简化 打包发布

介绍 在 Spring Boot 中&#xff0c;创建多模块项目可以帮助我们将项目拆分成多个相对独立、可重用的模块&#xff0c;从而使代码结构更清晰&#xff0c;便于管理和维护。通常&#xff0c;这样的做法可以提高开发效率&#xff0c;并且更易于进行版本控制和分布式部署。 项目结…

MySQL 数据库之表操作

1. 创建表 CREATE TABLE table_name ( field1 datatype, field2 datatype, field3 datatype ) [character set 字符集 collate 校验规则 engine 存储引擎];field 表示列名datatype 表示列的类型character set 字符集&#xff0c;如果没有指定字符集&#xff0c;则以所在数据库…

Git零基础到入门

一、开始工作区 clone: 克隆一个仓库到新的目录。 git clone https://github.com/username/repository.git init: 创建一个新的空 Git 仓库或重新初始化现有的仓库,新建git项目。 //创建项目两种方式 //一、本地项目自己创建项目&#xff0c;先创建好工作文件夹&#xff0c;通…

【R78/G15 开发板测评】串口打印 DHT11 温湿度传感器、DS18B20 温度传感器数据,LabVIEW 上位机绘制演化曲线

【R78/G15 开发板测评】串口打印 DHT11 温湿度传感器、DS18B20 温度传感器数据&#xff0c;LabVIEW 上位机绘制演化曲线 主要介绍了 R78/G15 开发板基于 Arduino IDE 环境串口打印温湿度传感器 DHT11 和温度传感器 DS18B20 传感器的数据&#xff0c;并通过LabVIEW上位机绘制演…

Chromium Mojo(IPC)进程通信演示 c++(2)

122版本自带的mojom通信例子associated-interface 仅供学习参考&#xff1a; codelabs\mojo_examples\02-associated-interface-freezing 一、目录结构如图&#xff1a; 二、interface.mojom接口 1、codelabs\mojo_examples\mojom\interface.mojom // Copyright 2023 The C…

SSL证书以及实现HTTP反向代理

注意&#xff1a; 本文内容于 2024-11-09 19:20:07 创建&#xff0c;可能不会在此平台上进行更新。如果您希望查看最新版本或更多相关内容&#xff0c;请访问原文地址&#xff1a;SSL证书以及实现HTTP反向代理。感谢您的关注与支持&#xff01; 之前写的HTTP反向代理工具&…

「Mac畅玩鸿蒙与硬件32」UI互动应用篇9 - 番茄钟倒计时应用

本篇将带你实现一个番茄钟倒计时应用&#xff0c;用户可以设置专注时间和休息时间的时长&#xff0c;点击“开始专注”或“开始休息”按钮启动计时&#xff0c;应用会在倒计时结束时进行提醒。番茄钟应用对于管理时间、提升工作效率非常有帮助&#xff0c;并且还会加入猫咪图片…

u盘怎么重装电脑系统_u盘重装电脑系统步骤和详细教程【新手宝典】

u盘怎么重装电脑系统&#xff1f;一个u盘怎么重装电脑系统呢&#xff0c;需要将u盘制作成u盘启动盘pe&#xff0c;然后通过U盘启动盘进入pe进行安装系统&#xff0c;下面小编就教大家u盘重装电脑系统步骤和详细教程。 u盘启动是什么意思&#xff1f; U盘启动盘是一种具有特殊功…

SQL注入攻击及其在SpringBoot中使用MyBatisPlus的防范策略

SQL注入攻击及其在SpringBoot中使用MyBatisPlus的防范策略 随着互联网技术的飞速发展&#xff0c;Web应用的安全问题日益凸显&#xff0c;其中SQL注入攻击是最常见的安全威胁之一。SQL注入攻击不仅可能导致敏感数据泄露&#xff0c;还可能引发数据篡改、服务中断等严重后果。本…

Typora导出pdf手动分页和设置字体样式

手动分页 <div style"page-break-after: always;"></div>鼠标点击代码才会显示&#xff0c;不点击会隐藏。导出pdf时&#xff0c;该位置会分页 设置字体大小、加粗、居中、空格 <p style"font-size:30px; font-weight: bold; text-align: cen…

简简单单的UDP

前言 上一篇了解了TCP的三次握手过程&#xff0c;目的、以及如何保证可靠性、序列号与ACK的作用&#xff0c;最后离开的时候四次挥手的内容&#xff0c;这还只是TCP内容中的冰山一角&#xff0c;是不是觉得TCP这个协议非常复杂&#xff0c;这一篇我们来了解下传输层另外一个协…

如何编写和运行go语言单元测试?

单元测试可以帮助开发者及时发现代码中的错误&#xff0c;提高代码质量&#xff0c;当代码库变更时&#xff0c;单元测试可以快速发现新引入的回归错误&#xff1b;在 Go 语言中&#xff0c;单元测试是确保代码质量和稳定性的重要工具。Go 提供了一个强大的 testing 包&#xf…

淘宝/天猫按图搜索商品:taobao.item_search_img API的奇幻之旅

在这个看脸的时代&#xff0c;我们不仅对人要看颜值&#xff0c;连买东西都要“看脸”了。没错&#xff0c;我说的就是淘宝/天猫的按图搜索商品功能——taobao.item_search_img API。这个功能就像是电商平台的“人脸识别”&#xff0c;只不过它认的是商品的颜值。下面&#xff…

hive表批量造数据

目录 1 . 使用 INSERT INTO 从已有表批量插入数据2. 使用 INSERT OVERWRITE 从文件或目录导入数据3. 使用 Hive 中的 SELECT 语句生成数据4. 使用 RAND() 或 UUID() 生成随机数据5. 使用 hive 的自定义 UDF 生成批量数据6. 使用 Python 脚本结合 Hive 进行数据生成7. 使用 hive…

软件工程 软考

开发大型软件系统适用螺旋模型或者RUP模型 螺旋模型强调了风险分析&#xff0c;特别适用于庞大而复杂的、高风险的管理信息系统的开发。喷泉模型是一种以用户需求为动力&#xff0c;以对象为为驱动的模型&#xff0c;主要用于描述面向对象的软件开发过程。该模型的各个阶段没有…