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…...

关于学习计算机的心得与体会
也是隔了一周没有发文了,最近一直在准备期末考试,后来想了很久,学了这么久的计算机,这当中有些收获和失去想和各位正在和我一样在学习计算机的路上的老铁分享一下,希望可以作为你们碰到困难时的良药。先叠个甲…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...

Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...
现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?
现有的 Redis 分布式锁库(如 Redisson)相比于开发者自己基于 Redis 命令(如 SETNX, EXPIRE, DEL)手动实现分布式锁,提供了巨大的便利性和健壮性。主要体现在以下几个方面: 原子性保证 (Atomicity)ÿ…...

R 语言科研绘图第 55 期 --- 网络图-聚类
在发表科研论文的过程中,科研绘图是必不可少的,一张好看的图形会是文章很大的加分项。 为了便于使用,本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中,获取方式: R 语言科研绘图模板 --- sciRplothttps://mp.…...
Modbus RTU与Modbus TCP详解指南
目录 1. Modbus协议基础 1.1 什么是Modbus? 1.2 Modbus协议历史 1.3 Modbus协议族 1.4 Modbus通信模型 🎭 主从架构 🔄 请求响应模式 2. Modbus RTU详解 2.1 RTU是什么? 2.2 RTU物理层 🔌 连接方式 ⚡ 通信参数 2.3 RTU数据帧格式 📦 帧结构详解 🔍…...