C++的左值、右值、左值引用和右值引用
目录
- 左值和右值
- 左值引用
- 右值引用
参考《现代C++语言核心特性解析》
以下加粗文字都是摘自本书。
左值和右值
左值和右值得概念在C++98就出现了,根据字面意思理解就是:左值是表达式等号左边的值,右值是表达式等号右边的值。
int x = 1;
int y = 3;
int z = x + y;
其中,x、y和z是左值,1、3、x + y的值是右值。
但是用这种方式去判断太过简单,而且不准确,例如:
int b = a;
其中a是左值不是右值。
摘自《现代C++语言核心特性解析》:在C++中所谓的左值一般是指一个指向特定内存的具有名称的值(具名对象),它有一个相对稳定的内存地址,并且有一段较长的生命周期。而右值则是不指向稳定内存地址的匿名值(不具名对象),它的生命周期很短,通常是暂时性的。基于这一特征,我们可以用取地址符&来判断左值和右值,能取到内存地址的值为左值,否则为右值。还是以上面的代码为例,因为&a和&b都是符合语法规则的,所以a和b都是左值,而&1在GCC中会给出“lvalue required as unary ‘&’ operand”错误信息以提示程序员&运算符需要的是一个左值。
总结:能取到内存地址的值是左值,否则为右值。
但有时候通过直觉去判断是不准确的,比如:
int x = 1;int get_val()
{return x;
}void set_val(int val)
{x = val;
}int main()
{x++;++x;int y = get_val();set_val(6);
}
在上面的代码中,x++是一个右值,++x是一个左值。因为对于后置++,编译器会生成一份x的临时复制,然后才对x自增,返回临时复制的内容,所以后置++是右值。
int a = &x++; // 编译失败
int a = &++x; // 编译成功
对于get_val函数,x作为全局变量是一个左值,但在返回x值时编译器会产生一份x的临时复制,还是右值。
int a = &get_val(); // 编译失败
对于set_val函数,set_val(6)中的6是一个右值,但是进入函数之后val变成一个左值,所以可以对val取地址。
void set_val(int val)
{int a = &val; // 编译成功x = val;
}
还需要强调一点,通常字面常量都是右值,但是字符串常量除外。
int x = &1; // 编译失败
auto p = &"hello world" // 编译成功
毕竟&"hello world"的语法也很少看到。但是这段代码是可以编译成功的,其实原因仔细想来也很简单,编译器会将字符串字面量存储到程序的数据段中,程序加载的时候也会为其开辟内存空间,所以我们可以使用取地址符&来获取字符串字面量的内存地址。
左值引用
左值引用的出现使C++编程脱离了使用指针的危险,当我们需要将一个对象作为函数的参数时,会使用左值引用,这种方式会免去创建临时对象的操作。
左值引用细分可以分为非常量左值引用和常量左值引用。
非常量左值只能引用左值,不能引用右值。
常量左值可以引用左值,也可以引用右值。
int &a = 1; // 编译失败int x = 1;const int& b = 1; // 常量左值引用右值const int& c = x; // 常量左值引用左值
请注意,虽然在结果上const int &x =11和const int x = 11是一样的,但是从语法上来说,前者是被引用了,所以语句结束后11的生命周期被延长,而后者当语句结束后右值11应该被销毁。虽然常量左值引用可以引用右值的这个特性在赋值表达式中看不出什么实用价值,但是在函数形参列表中却有着巨大的作用。一个典型的例子就是复制构造函数和复制赋值运算符函数,通常情况下我们实现的这两个函数的形参都是一个常量左值引用,例如:
#include <iostream>class MyClass
{
public:MyClass(){}MyClass(const MyClass&){}MyClass& operator=(const MyClass&){return *this;}
};MyClass MakeMyClass()
{return MyClass();
}int main()
{MyClass x1;MyClass x2(x1);MyClass x3(MakeMyClass());x3 = MakeMyClass();system("pause");return 0;
}
以上代码可以通过编译,但是如果把拷贝构造函数和赋值构造函数形参的常量性去掉,就编译不过了。因为非常量左值无法引用MakeMyClass()返回的右值,所以常量左值引用右值是一条非常棒的特性,但是也有弊端。一旦使用了常量左值,就不能在函数里去修改对象的内容(强制类型转换除外),所以在这种情况下诞生了右值引用。
右值引用
右值引用是一种引用右值且只能引用右值的方法。
左值引用是在类型后面加&,右值引用是在类型后面加&&。
int x = 1;int& a = x; // 左值引用int&& k = 2; // 右值引用
右值引用最大的特点是延长了右值的生命周期。
举例:
#include <iostream>class BigMemoryPool
{
public:BigMemoryPool(){std::cout << "普通构造函数" << std::endl;}~BigMemoryPool(){std::cout << "析构函数" << std::endl;}BigMemoryPool(const BigMemoryPool &other){std::cout << "拷贝构造函数" << std::endl;}void show(){std::cout << "调用函数" << std::endl;}
};BigMemoryPool Make()
{BigMemoryPool resultA;return resultA;
}int main()
{BigMemoryPool &&my_pool = Make();my_pool.show();return 0;
}
请注意,用GCC编译以上代码需要加上命令行参数-fno-elide-constructors用于关闭函数返回值优化(RVO)。因为GCC的RVO优化会减少复制构造函数的调用,不利于语言特性实验。
但是我用G++编译,命令行是
g++ -std=c++11 -fno-elide-constructors main.cpp -o main
以上代码会调用三次构造函数,注释中的1、2、3会各调用一次构造函数。
输出结果:
PS C:\Users\zh'n\Desktop\新建文件夹> g++ -std=c++11 -fno-elide-constructors main.cpp -o main
PS C:\Users\zh'n\Desktop\新建文件夹> ./main
普通构造函数
拷贝构造函数
析构函数
拷贝构造函数
析构函数
调用函数
析构函数
如果我们将"3“改为
BigMemoryPool &&my_pool = Make();
会调用两次构造函数,第一次是Make函数中resultA的默认构造,第二次是return resultA引发的复制构造。不同的是,由于x2是一个右值引用,引用的对象是函数Make返回的临时对象,因此该临时对象的生命周期得到延长,所以我们可以在BigMemoryPool &&my_pool = Make();语句结束后继续调用show函数而不会发生任何问题。
输出结果
PS C:\Users\zh'n\Desktop\新建文件夹> g++ -std=c++11 -fno-elide-constructors main.cpp -o main
PS C:\Users\zh'n\Desktop\新建文件夹> ./main
普通构造函数
拷贝构造函数
析构函数
调用函数
析构函数
对性能敏感的读者应该注意到了,延长临时对象生命周期并不是这里右值引用的最终目标,其真实目标应该是减少对象复制,提升程序性能。
相关文章:
C++的左值、右值、左值引用和右值引用
目录 左值和右值左值引用右值引用 参考《现代C语言核心特性解析》 以下加粗文字都是摘自本书。 左值和右值 左值和右值得概念在C98就出现了,根据字面意思理解就是:左值是表达式等号左边的值,右值是表达式等号右边的值。 int x 1; int y …...
罗技鼠标使用接收器和电脑重新配对
罗技鼠标使用接收器和电脑重新配对 文章目录 罗技鼠标使用接收器和电脑重新配对1\. 前言2\. 安装软件3\. 进行配对3.1. 取消之前的配对3.2. 重新配对3.3 配对完成 4\. 报错4.1. 重新配对时显示配对未成功 1. 前言 罗技的鼠标出厂的时候,默认的是将通道一设置为接收…...
高项备考葵花宝典-项目进度管理输入、输出、工具和技术(下,很详细考试必过)
项目进度管理的目标是使项目按时完成。有效的进度管理是项目管理成功的关键之一,进度问题在项目生命周期内引起的冲突最多。 小型项目中,定义活动、排列活动顺序、估算活动持续时间及制定进度模型形成进度计划等过程的联系非常密切,可以视为一…...
GumbleSoftmax感性理解--可导式输出随机类别
GumbleSoftmax 本文不涉及GumbleSoftmax的具体证明和推导,有需要请参见1,只是从感性角度来直观讲解为何要引入GumbleSoftmax,同时又为什么不用Gumblemax。 GumbleSoftmax提出是为了应对分布采样不可导的问题。举例而言,我们从网络…...
ROS gazebo 机器人仿真,环境与robot建模,添加相机 lidar,控制robot运动
b站上有一个非常好的ros教程234仿真之URDF_link标签简介-机器人系统仿真_哔哩哔哩_bilibili,推荐去看原视频。 视频教程的相关文档见:6.7.1 机器人运动控制以及里程计信息显示 Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程 本文对视频教程…...
人体关键点检测3:Android实现人体关键点检测(人体姿势估计)含源码 可实时检测
目录 1. 前言 2.人体关键点检测方法 (1)Top-Down(自上而下)方法 (2)Bottom-Up(自下而上)方法: 3.人体关键点检测模型训练 4.人体关键点检测模型Android部署 (1) 将Pytorch模型转换ONNX模型 (2) 将ONNX模型转换…...
踩坑记录:uniapp中scroll-view的scroll-top不生效问题;
情景描述: 最近在uniapp项目中用到scroll-view内置组件,有需求是在页面下拉刷新后,让scroll-view组件区域的显示内容置顶,也就是scroll-view区域的内容恢复不滑动的状态; 补充:下拉刷新操作scroll-view组件…...
YOLOX 学习笔记
文章目录 前言一、YOLOX贡献和改进二、YOLOX架构改进总结 前言 在计算机视觉领域,实时对象检测技术一直是一个热门的研究话题。YOLO(You Only Look Once)系列作为其中的佼佼者,以其高效的检测速度和准确性,广泛应用于…...
第3节:Vue3 v-bind指令
实例: <template><div><button v-bind:disabled"isButtonDisabled">点击我</button></div> </template><script> import { ref } from vue;export default {setup() {const isButtonDisabled ref(false);ret…...
Token 和 N-Gram、Bag-of-Words 模型释义
ChatGPT(GPT-3.5)和其他大型语言模型(Pi、Claude、Bard 等)凭何火爆全球?这些语言模型的运作原理是什么?为什么它们在所训练的任务上表现如此出色? 虽然没有人可以给出完整的答案,但…...
【go语言实践】基础篇 - 流程控制
if语句 go里面if不需要括号将条件表达式包含起来,这与python也有点类似 if 条件表达式 { } if num > 18 {// ... } else if num > 20 {// ... } else {// ... }需要注意的是go支持在if的条件表达式中直接定义一个变量,变量的作用域只在if范围内…...
Linux:gdb的简单使用
个人主页 : 个人主页 个人专栏 : 《数据结构》 《C语言》《C》《Linux》 文章目录 前言一、前置理解二、使用总结 前言 gdb是Linux中的调试代码的工具 一、前置理解 我们都知道要调试一份代码,这份代码的发布模式必须是debug。那你知道在li…...
NestJS的微服务实现
1.1 基本概念 微服务基本概念:微服务就是将一个项目拆分成多个服务。举个简单的例子:将网站的登录功能可以拆分出来做成一个服务。 微服务分为提供者和消费者,如上“登录服务”就是一个服务提供者,“网站服务器”就是一个服务消…...
Debian 终端Shell命令行长路径改为短路径
需要修改bashrc ~/.bashrc先备份一份 cp .bashrc bashrc.backup编辑bashrc vim ~/.bashrc可以看到bashrc内容为 # ~/.bashrc: executed by bash(1) for non-login shells. # see /usr/share/doc/bash/examples/startup-files (in the package bash-doc) # for examples# If…...
Ansible变量是什么?如何实现任务的循环?
Ansible 利用变量存储整个 Ansible 项目文件中可重复使用的值,从而可以简化项目的创建和维护,并减少错误的发生率。在定义Ansible变量时,通常有如下三种范围的变量: global范围:从命令行或Ansible配置中设置的变量&am…...
随机梯度下降的代码实现
在单变量线性回归的机器学习代码中,我们讨论了批量梯度下降代码的实现,本篇将进行随机梯度下降的代码实现,整体和批量梯度下降代码类似,仅梯度下降部分不同: import numpy as np import pandas as pd import matplotl…...
渐进推导中常用的一些结论
标题很帅 STAR-RIS Enhanced Joint Physical Layer Security and Covert Communications for Multi-antenna mmWave Systems文章末尾的一个推导。 lim M → ∞ ∥ Φ ( w k ⊗ Θ r ) Ω r w H g ∗ ∥ 2 2 M lim M → ∞ Tr ( g T Ω r w ( w k ⊗ Θ r ) H Φ H Φ…...
网络安全等级保护V2.0测评指标
网络安全等级保护(等保V2.0)测评指标: 1、物理和环境安全 2、网络和通信安全 3、设备和计算安全 4、应用和数据安全 5、安全策略和管理制度 6、安全管理机构和人员 7、安全建设管理 8、安全运维管理 软件全文档获取:点我获取 1、物…...
java中list的addAll用法详细实例?
List 的 addAll() 方法用于将一个集合中的所有元素添加到另一个 List 中。下面是一个详细的实例,展示了 addAll() 方法的使用: java Copy code import java.util.ArrayList; import java.util.List; public class AddAllExample { public static v…...
关于学习计算机的心得与体会
也是隔了一周没有发文了,最近一直在准备期末考试,后来想了很久,学了这么久的计算机,这当中有些收获和失去想和各位正在和我一样在学习计算机的路上的老铁分享一下,希望可以作为你们碰到困难时的良药。先叠个甲…...
浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)
✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义(Task Definition&…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...
【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...
dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...
质量体系的重要
质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络…...
CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...
RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
