C++11左值和右值、左值引用和右值引用浅析

news2024/11/28 14:51:12

从字面意思来讲,左值就是“能用在赋值语句等号左侧的内容(它得代表一个地址)”;右值就是不能作为左值的值,即右值不能出现在赋值语句中等号的左侧。C++中的一条表达式,要么就是右值,要么就是左值,不可能两者都是。但是一个左值有时候又能被当做右值使用(即这个对象具有右值属性,但并不是右值)。如 i = i+1这条语句:对象i在赋值语句等号的右侧时,用的是这个对象的值(具有右值属性),这个对象在赋值语句左侧时,用的是对象在内存中的地址(具有左值属性)。所以一个对象可以同时具有左值和右值属性^{[1]}

赋值运算符左侧的对象就是一个左值,但实质上整个赋值语句的结果仍然是左值,只不过在使用printf函数或者cout等场景进行输出的时候被当做右值使用(即左值具有右值属性)。

在左值表达式和右值表达式中,不要被“表达式”三个字所迷惑,如一个变量也可以称之为表达式、因此可以这样理解,左值表达式就是左值,右值表达式就是右值^{[1]}

左值引用(绑定到左值):引用那些希望改变值的对象,左值引用带一个"&"。

右值引用(绑定到右值):首先它也是一个引用,但是右值引用所侧重表达的意思往往是表示所引用对象的值在使用之后就无需保留了(如临时变量),右值引用带有两个“&&”。

左值引用就是绑定到左值的引用,右值引用就是绑定到右值的引用。一般来讲,右值引用其实主要是用来绑定到那些“即将销毁/临时的对象”上。右值引用也是引用。并且,右值引用虽然绑定到了右值对象上,但是右值引用本身还是个左值,毕竟右值引用也是位于等号左边的,如下代码是可以正常编译通过的。

#include <bits/stdc++.h>

using namespace std;

int main()
{
    int i = 1;
    int &&r1 = i++; // r1虽然是个右值引用,引用到了右值对象,但是r1本身还是个左值
    int &r5 = r1;   // 可以,说明r1本身是一个左值
    cout << r5 << endl;

    return 0;
}

返回左值引用的函数,连同赋值、下标、解引用和前置递增递减运算符等等,都是返回左值表达式(左值)的例子,因此可以讲一个左值引用绑定到这类表达式的结果上。

返回非引用类型的函数,连同算术、关系、位以及后置递增运算符,都生成右值,不能将一个左值引用绑定到这类表达式上,但是可以将一个const的左值引用或者一个右值引用绑定到这类表达式的结果上。

所有变量都要看成是左值,因为他们是有地址的,这类变量用右值绑定也绑定不上。

任何函数里的形参都是左值,即使在诸如“void (int &&)”这种写法里面,形参w的类型是右值引用(需要绑定到右值上),但是w本身还是一个左值。

右值引用引入的目的是提高程序运行的效率问题,,其手段是把复制对象操作变成移动对象操作

移动对象的概念如下:假设对象A不再使用了,那么就可以把对象A里面一些使用new分配的内存卡的所有权转给对象B,对于对象B而言,就不用再new出一些内存块了。把A种new出来的内存块直接转给B后,A 就把指向这些内存卡的指针清空一下。这就叫移动对象(把老对象里面的一些对象转移给了新对象)。也就是说,很多分配出去的内存并没有被回收而是转移给了新对象,这种把某一些内存块从原来的对象A转移给了新对象的操作就叫做移动

对象的移动操作是通过移动构造函数和移动赋值运算符来实现的,移动构造函数和移动赋值运算符的外观看起来与拷贝构造函数和复制运算符非常像,只不过移动构造函数和移动赋值运算符所需要的参数类型是“&&”这种右值引用类型

std::move函数虽然翻译成中文是移动的意思,但是这个函数实际上并没有做任何移动操作,而是把一个左值强制转换成右值(带来的结果就是一个右值引用可以绑定到这个转换得到的右值上面去了)。本来一个右值引用是无法绑定到左值上去的,但是经过move函数处理之后(将左值进行转换得到右值),这个右值引用就能绑定到原来的一个左值上面去了。有些函数的参数是一个右值引用,需要绑定到右值,此时也可以用move函数将左值转换成右值,转换结果就可以当做实参传递给该函数了。

通常情况下,如果需要移动语义,程序员必须自定义移动构造函数。当然对于一些简单的、不包含任何资源的类型来说,实现移动语义与否都无关紧要,因为对于这样的类型而言,移动就是拷贝,拷贝就是移动。移动语义往往构造的是资源型的类型,如智能指针,指针、文件流等

对于移动构造函数而言,抛出异常是件危险的事情。因为移动语义还没完成,一个异常却抛出来了,这可能会导致一些指针成为悬挂指针。因此程序员应当尽量编写不抛出异常的移动构造函数:通过为其添加一个noexcept关键字,可以保证移动构造函数抛出的异常会直接调用terminate终止程序运行而不是造成指针悬挂的问题^{[2]}

[1]《C++新经典》,王建伟编著

[2]《深入理解C++11:C++11新特性解析与应用》,IBM XL编译器开中国开发团队著

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

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

相关文章

钉钉监控Hippo4j线程池通知报警

&#x1f680; 线程池管理工具-Hippo4j &#x1f680; &#x1f332; AI工具、AI绘图、AI专栏 &#x1f340; &#x1f332; 如果你想学到最前沿、最火爆的技术&#xff0c;赶快加入吧✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;CSDN-Java领域优质创作者&#…

软考03海明校验码

文章目录 前言一、练习一二、练习二总结 前言 海明校验码可以用来纠正错误 公式&#xff1a; 2k-1≥nk n为数据位&#xff0c;k为校验位 编码规则&#xff1a;校验位为2的次方&#xff0c;其他为数据位。 一、练习一 数据位为4&#xff0c;校验位最小为多少位&#xff1f; 答&…

《微服务架构设计模式》第十二章 部署微服务应用

内容总结自《微服务架构设计模式》 部署微服务应用 一、部署模式分类二、编程语言特定的发布包格式1、概述2、利弊 三、将服务部署为虚拟机1、概览2、利弊 四、将服务部署为容器1、概述2、利弊3、K8S部署 五、Serverless部署1、概述2、利弊3、示例 六、总结 一、部署模式分类 …

计算机丢失msvcp140.dll是什么意思?哪个修复方法更推荐

打开photoshop软件的时候&#xff0c;计算报错丢失msvcp140.dll是什么意思&#xff1f;软件也无法正常启动运行&#xff0c;这个主要的原因就是电脑系统中的msvcp140.dll文件丢失或者损坏了&#xff0c;运行需要该文件的程序或应用程序时&#xff0c;操作系统无法找到该特定的动…

低代码都做了什么?怎么实现 Low-Code?

前言 低代码的概念早在很多很多年前就已经出现了&#xff0c;比如最早期的Dreamweaver 1.0&#xff0c;使用这种可视化编辑工具根本不需要投入较高的学习成本就可以轻松实现一个Web页面。而低代码最大的初衷也正是让开发者或用户减少编码时间&#xff0c;从而把更多的时间和精力…

Python应用实例(二)数据可视化(五)

数据可视化&#xff08;五&#xff09;制作全球地震散点图&#xff1a;JSON格式 1.地震数据2.查看JSON数据3.创建地震列表4.提取震级5.提取位置数据6.绘制震级散点图7.另一种指定图表数据的方式 下载一个数据集&#xff0c;其中记录了一个月内全球发生的所有地震&#xff0c;再…

Unity3d-路径巡逻

使用U3D实现的简单巡逻方法 游戏对象逐个向组成路径的节点进行直线移动两种巡逻方案 根据列表顺序移动&#xff0c;到达最后一个后&#xff0c;直接返回第一个&#xff0c;重新开始循环根据列表顺序移动&#xff0c;到达最后一个后&#xff0c;根据顺序反向移动到第一个&#…

用 Pytest+Appium+Allure 做 UI 自动化的那些事~(有点干)

目录 前言&#xff1a; Appium 不常见却好用的方法 Appium 直接执行 adb shell 方法 Appium 直接截取元素图片的方法 Appium 直接获取手机端日志 Appium 直接与设备传输文件 Pytest 与 Unittest 初始化上的区别 1.Pytest 与 unitest 类似&#xff0c;有些许区别&#x…

【嘉立创EDA】客户端版本嘉立创专业版半离线版更新方法

文章路标👉 文章解决问题主题内容工程文件备份保护问题新版本更新通知文章解决问题 1️⃣ 嘉立创EDA专业版,是时下越发常用的PCBA设计软件之一。该环境除了在规划的设计开发过程中,为响应各用户的建议、需求,其迭代更新速度也是很快。为了使用最新的功能,用户需要使用最…

C++学习——类和对象(一)

C语言和C语言最大的区别在于在C当中引入了面向对象的编程思想&#xff0c;想要完全了解c当中的类和对象&#xff0c;就要从头开始一点一点的积累并学习。 一&#xff1a;什么是面向对象编程 我们之前学习的C语言属于面向过程的编程方法。举一个简单的例子来说&#xff1a;面向过…

Linux地盘上AMD处理器称王了

近日资讯&#xff0c;尽管从全局来看&#xff0c;Linux系统份额远不及Windows&#xff0c;但在程序员、开发者、硬核玩家圈子&#xff0c;Linux则备受推崇。 来自Steam的最新数据显示&#xff0c;在Linux游戏用户中&#xff0c;AMD处理器的份额占据绝对优势&#xff0c;达到了…

Spring Cloud—GateWay之限流

RequestRateLimiter RequestRateLimiter GatewayFilter 工厂使用 RateLimiter 实现来确定是否允许当前请求继续进行。如果不允许&#xff0c;就会返回 HTTP 429 - Too Many Requests&#xff08;默认&#xff09;的状态。 这个过滤器需要一个可选的 keyResolver 参数和特定于…

cpuset.cpus.effective: no such file or directory (修改 docker cgroup 版本的方法)

要切换使用 v1 版 cgroup&#xff0c;需要做如下配置&#xff1a; vim /etc/default/grubGRUB_CMDLINE_LINUX"systemd.unified_cgroup_hierarchy0"update-grubreboot完美解决

【条件与循环】——matlab入门

目录索引 if&#xff1a;else与elseif&#xff1a; for&#xff1a; if&#xff1a; if 条件语句块 endelse与elseif&#xff1a; if 条件代码块 elseif 条件代码块 else 代码块 endfor&#xff1a; for 条件循环体 end在matlab里面类似的引号操作都是包头又包尾的。上面的c…

TypeScript基础篇 - TS介绍

目录 Typescript的定义 type.ts 深入了解Typescript Typescript应该学到什么程度&#xff1f; Typescript学习方法 如何学好TS 小节&#xff1a;常见学习误区 一张ai生成图~ Typescript的定义 2012年微软发布的一门编程语言 Transcompiler【翻译编译器】Typescript——…

操作符详解(2)

文章目录 8. 条件操作符9. 逗号表达式10. 下标引用、函数调用和结构成员11. 表达式求值11.1 隐式类型转换11.2 算术转换11.3 操作符的属性 附&#xff1a; 8. 条件操作符 exp1 ? exp2 : exp3 int main() {int a 0;int b 0;if (a > 5){b 3;}else{b -3;}//(a > 5) ? …

【Elasticsearch】黑马旅游案例

目录 4.黑马旅游案例 4.1.酒店搜索和分页 4.1.1.需求分析 4.1.2.定义实体类 4.1.3.定义controller 4.1.4.实现搜索业务 4.2.酒店结果过滤 4.2.1.需求分析 4.2.2.修改实体类 4.2.3.修改搜索业务 4.3.我周边的酒店 4.3.1.需求分析 4.3.2.修改实体类 4.3.3.距离排序…

linux学习笔记(1)----基础知识

1.linux用户 ubuntu有三种用户&#xff1a; 1&#xff09;初次创建的用户 2&#xff09;root用户 3&#xff09;普通用户 linux用户记录在etc/passwd这个文件内 linux用户密码在etc/shadow这个文件内 2.linux用户组 为了方便管理&#xff0c;将用户进行分组&#xff0c…

.NET Native AOT的静态库与动态库

.NET不仅可以使用 C静态库与动态库&#xff0c;也可以将.NET实现的函数导出为C静态库与动态库。在没有Native Aot之前&#xff0c;.NET只能通过P/Invoke享受C/C生态&#xff0c;而在Native Aot之后&#xff0c;不仅可以享受这些生态&#xff0c;还可以开发SDK供其他语言调用。 …

多层、六边形、洋葱和clean架构简介

想想回到过去的美好时光&#xff0c;根本没有所谓架构&#xff0c;那些日子是多么幸福啊&#xff0c;只有了解 GoF 模式&#xff0c;你就能称自己为架构师。 然而&#xff0c;计算机变得更加强大&#xff0c;用户的需求增加&#xff0c;导致应用程序的复杂性增加。 开发人员解决…