C++11——右值引用和移动语义

左值和右值

在C++11之前,我们很少去关注左值和右值这一概念,但是在C++11中,加入了一个非常重要的语法:右值引用

左值和右值,一般来说可以当作字面意思,左值是经常出现在表达式左边的值,右值是经常出现在表达式右边的值。但是,仅仅靠这一判断方式是绝对错误的。左值和右值的本质区别是能否被取地址,能被取地址的值为左值,不能被取地址的值为右值。

我们也可以换一种方式来定义左值和右值:

暂时不会被销毁的值为左值

即将被销毁的值为右值

如何理解这一定义?

我们在匿名对象中学到,我们传值或者赋值的时候可以用匿名对象来传值,这个匿名对象的生命周期只在这一行,这一行过后匿名对象便会被销毁

而在学习完类与对象之后,我们可以把所有内置类型的赋值都抽象为一个匿名对象的赋值

而这些生命周期极短,没有一个具体的变量名称(即为匿名),无法被其他地方调用的值便称为右值

同样,在函数中,也有一些甚至不出现在文本中的右值

此时我们便可以很清晰的发现一个结论:

 


右值引用

右值引用的定义 

我们知道,C++中为了避免函数传参的时候反复赋值,我们要尽可能去使用引用传参来提高效率。但是对于返回值的右值情况,我们并没有很好的办法去进行引用传参。因为如果返回值是一个引用返回,我们必须new一个新空间来返回,在大多数情况是不满足需求的。而如果采用一般的传值返回,因为右值无法取地址,无法进行引用,就不可避免会进行拷贝右值,降低效率。

于是在C++11中,就有了一个新语法:右值引用。顾名思义,右值引用是一种可以引用右值的语法,当我们进行右值引用的时候,这个右值的空间便可以被我们访问,同样该右值的空间也可以被我们修改,就相当于给将死之人一段新的寿命,让其生命周期延长一段时间直到定义右值引用时的名称也被销毁为止

右值引用的长相如下: 

 我们可以随意访问右值的空间 

但是右值引用的使用场景并非在此,我们无需过多关注右值引用的那块空间是如何变化的 

右值引用的使用场景——移动构造

我们在进行拷贝构造的时候,会涉及一个问题——浅拷贝和深拷贝。浅拷贝好说,把所有值全部赋值过来便可以了,但是深拷贝,比如所有的container,我们要重新开辟节点,对每一个节点赋值,效率一下就下来了

而且一般的拷贝构造是无奈之举,如果我们用一串右值进行拷贝构造,右值本来就是一个临时变量,我们拷贝完这个右值,接着又销毁这个右值,那岂不是多此一举?

这个时候便开始怀疑和思考:

这个时候,右值引用便迎来了他的高光时刻
我们再来思考一下我们的需求:

  • 如果传入值是左值,我们深拷贝这个左值,左值不发生任何变化
    如果传入值是右值,我们直接将这个右值塞到对象里,不需要管右值的死活
  • 也就是说,我们只需要做两件事:识别传入的是左值还是右值,如何以最高效的方法将右值塞到对象里。

对于第一个问题, 很简单,我们只需要用重载函数将参数写为右值引用,这样这一函数接口只能识别右值

对于第二个问题,也很简单,因为右值的死活我们不关心,我们干脆直接交换右值和对象便可以了

这样,只需要一次交换,右值的值便全部到了左值身上,而因为右值马上会销毁,所以其为任何值都没有关系,我们也不需要关心,如此便把一个深拷贝的问题变成了一个单个值交换的问题

而因为swap是需要左右值都是可以修改的,所我们传入参数不能传入const的右值 

效率上的意义

光只了解移动构造,其实并没有多大的价值,因为我们平时也不太常用初始化列表进行构造。但是,对于函数的传参,这个意义就大了

还是刚刚的例子

如果我们定义了右值引用的构造函数,在作为右值的临时变量去构造新的string时,便会调用移动构造而非深拷贝,通过这种方式来提高传值的效率


移动语义

此刻,我们已经接触到了一个概念:移动构造。但是移动构造仍然会出现一些小问题,例如:

class A
{
public:
	A()
	{}

    //深拷贝构造
	A(const A& a)
	{
		cout << "深拷贝构造" << endl;
	}

    //移动构造
	A(A&& a)
	{
		cout << "移动构造" << endl;
	}
};

//右值引用
void func(A&& right_val)
{
    //这个是移动构造还是深拷贝构造?
	A tmp(right_val);
}

int main()
{
	func(A());
}

我们在一个函数中传入右值引用,但是我们用该右值来构造的时候,我们惊奇地发现,最终调用的是深拷贝构造

 

原因其实很简单。我们在左右值的定义中已经说到,左右值的区别是其能否被取地址,或者其是否是即将被销毁的变量,但是这里的right_val,虽然是一个右值引用,但是它已经成为了一个类型为A&&的新变量,而且其只有在函数走完后才会被销毁,其完完全全是一个左值

所以自然,我们用其进行构造的时候,当然会调用传入左值时的拷贝构造函数,而非移动构造函数。

有没有一些方法来解决呢?当然

Move

右值引用,听名字就知道,只可以引用右值,无法引用左值。

 

但是我们非要引用左值,怎么办?

这个时候便要介绍一个新的函数:move

 

move的作用是传入左值后返回一个右值,但是其只是返回值是个右值,并非将左值的属性改成了右值,不过返回值的右值和原本的左值享有共同一块空间,只有属性发生了变化

int main()
{
	int a = 1;//没毛病
	int&& b = 2;//没毛病
	int&& c = move(a);//现在没毛病了
	
	int&& d = a;//a仍是一个左值,仍无法被右值引用

	//验证右值的a和左值的a共用一块空间
	swap(b, c);
	cout << a;
	cout << b;
	cout << c;
}

对于move的具体实现,我们就不要去细究了,我们只需要直到以上的概念,总结为:

  1. move的作用是传入左值,只更改过属性的右值
  2. 传入的值和返回的值共享同一块区间
  3. 传入后左值仍为左值,不会变为右值 

 所以,我们想采用右值引用时,只需要加一个move便可以实现

//右值引用
void func(A&& right_val)
{
    //右值引用
	A tmp(move(right_val));
}

int main()
{
	func(A());
}

完美转发

除了move,C++还提供了另一个函数——forward来进行类型的识别。

 

看了看字典的解释,便可以简单理解forward的意思

forward函数用于保留其在传参过程中原生类型的属性,比如int&&的原生类型属性是int的右值,而非int&&的左值

//右值引用
void func(A&& right_val)
{
    //右值引用
	A tmp(forward(right_val));
}

int main()
{
	func(A());
}

如此一来,如果传入的是左值,那便会采用左值的深拷贝构造,如果传入的是右值,便会采用右值的移动构造,就避免了move不管左值右值最终都转化为右值的问题

那就有人要问了,这不只有传入右值吗?传入左值怎么通过这个接口呢?

那你就问到点了,这便是C++的另一新语法:万能引用 

万能引用 

在模板里,我们使用typename&&作为参数时,并不是代表其是一个右值引用传参,而是代表着其左值引用传参和右值引用传参都可以接收 

void Fun(int& x) { cout << "左值引用" << endl; }
void Fun(const int& x) { cout << "const 左值引用" << endl; }
void Fun(int&& x) { cout << "右值引用" << endl; }
void Fun(const int&& x) { cout << "const 右值引用" << endl; }

// 模板中的&&不代表右值引用,而是万能引用,其既能接收左值又能接收右值。
// 模板的万能引用只是提供了能够接收同时接收左值引用和右值引用的能力,
// 但是引用类型的唯一作用就是限制了接收的类型,后续使用中都退化成了左值,
// 我们希望能够在传递过程中保持它的左值或者右值的属性, 就需要用我们学习的完美转发
template<typename T>
void PerfectForward(T&& t)
{
	Fun(t);
}
int main()
{
	PerfectForward(10); // 右值
	int a;
	PerfectForward(a); // 左值
	PerfectForward(std::move(a)); // 右值
	const int b = 8;
	PerfectForward(b); // const 左值
	PerfectForward(std::move(b)); // const 右值
	return 0;
}

以上便是右值引用和移动语义的所有内容因为不涉及太多代码,就不放代码链接了

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

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

相关文章

【开源视频联动物联网平台】写一个物联网项目捐献给Dromara组织

一、平台简介 MzMedia开源视频联动物联网平台&#xff0c;简单易用&#xff0c;更适合中小企业和个人学习使用。适用于智能家居、农业监测、水利监测、工业控制&#xff0c;车联网&#xff0c;监控直播&#xff0c;慢直播等场景。 支持抖音&#xff0c;视频号等主流短视频平台…

Linux--系统结构与操作系统

文章目录 冯诺依曼体系结构为什么要有内存&#xff1f;场景一 操作系统何为管理&#xff1f; 冯诺依曼体系结构 冯诺依曼体系结构是计算机体系结构的基本原理之一。它将程序和数据都以二进制形式存储&#xff0c;以相同的方式处理和存取。 上图是冯诺依曼体系结构的五大组成部…

处理机调度与作业调度

处理机调度 一个批处理型作业&#xff0c;从进入系统并驻留在外存的后备队列上开始&#xff0c;直至作业运行完毕&#xff0c;可能要经历如下的三级调度 高级调度 也称为作业调度、长程调度、接纳调度。调度对象是作业 主要功能&#xff1a; 挑选若干作业进入内存 为作业创建…

git push 报错 error: src refspec master does not match any 解决

git报错 ➜ *** git:(main) git push -u origin "master" error: src refspec master does not match any error: failed to push some refs to https://gitee.com/***/***.git最新版的仓库初始化后 git 主分支变成了 main 方法 1.把 git 默认分支名改回 master …

Sentinel的一些知识二

11231041 下一个是 熔断规则是异常数 和异常比例一样 只是换成了异常个数 1秒内的异常数有3个&#xff0c;就熔断2秒 下一步 进行压力测试 11231343 热点规则 没懂这个热点规则存在的意义 某个用户访问过于频繁&#xff0c;对其进行限制&#xff0c;给其他用户访问的机…

mysql中删除数据后,新增数据时id会跳跃,主键自增id不连续

引言&#xff1a; 在使用MySQL数据库时&#xff0c;有时候我们需要删除某些记录&#xff0c;但是删除记录后可能会导致表中的id不再连续排序。 如何实现删除记录后让id重新排序的功能。 如图&#xff1a; 删除数据后&#xff0c;中间的id不会自动连续。 下面有两种方法进行重…

万界星空科技仓库管理wms系统

企业在管理库存时&#xff0c;尤其是生产制造企业&#xff0c;使用传统方式比如纸笔、Excel 管理库存&#xff0c;由于工具和信息化存在局限&#xff0c;导致在管理库存时出现如下问题&#xff1a; 1、通过纸笔记录出入库申请&#xff0c;人为手动计算易出错&#xff0c;数据易…

金石工程项目管理系统 SQL注入漏洞复现

0x01 产品简介 金石工程项目管理软件是一款工程项目管理软件,专门针对建筑工程项目开发,可以用于各种工地的项目管理。 0x02 漏洞概述 金石工程项目管理系统TianBaoJiLu.aspx接口处存在SQL注入漏洞&#xff0c;攻击者可通过该漏洞获取数据库中的信息&#xff08;例如&#xff…

外贸B2B自建站怎么建?做海洋建站的方法?

如何搭建外贸B2B自建站&#xff1f;外贸独立站建站方法有哪些&#xff1f; 对于许多初次涉足者来说&#xff0c;搭建一个成功的外贸B2B自建站并不是一件轻松的任务。海洋建站将为您详细介绍如何有效地建设外贸B2B自建站&#xff0c;让您的国际贸易之路更加畅通无阻。 外贸B2B…

TR转发路由器测评—云企业网实现跨地域跨VPC的网络互通测评实战【阿里云产品测评】

文章目录 一.转发路由器 Transit Router 测评1.1 准备阶段1.2 本文测评收获1.3 什么是云企业网实例、转发路由器实例和云数据传输服务 二.使用云企业网实现跨地域跨VPC的网络互通2.2 **测试连通性**2.3 网络拓扑如下&#xff1a; 心得&#xff1a;总结&#xff1a; 声明&#x…

LeetCode Hot100 438.找到字符串中所有字母异位词

题目&#xff1a; 给定两个字符串 s 和 p&#xff0c;找到 s 中所有 p 的 异位词 的子串&#xff0c;返回这些子串的起始索引。不考虑答案输出的顺序。 异位词 指由相同字母重排列形成的字符串&#xff08;包括相同的字符串&#xff09;。 代码&#xff1a; class Solution …

.Net 8 Blazor下 Auto交互渲染模式试用

一、环境 C:\Users\zhuji>dotnet --version 8.0.100C:\Users\zhuji>dotnet --list-sdks 5.0.403 [C:\Program Files\dotnet\sdk] 6.0.404 [C:\Program Files\dotnet\sdk] 8.0.100 [C:\Program Files\dotnet\sdk] Microsoft Visual Studio Enterprise 2022 (64 位) - Cu…

『Jmeter超级干货』| Linux下Jmeter安装配置、脚本设计执行、监控及报告完整过程

『Jmeter超级干货』| Linux下Jmeter安装配置、脚本设计执行、监控及报告完整过程 1 JDK安装部署1.1 JDK下载1.2 JDK配置 2 Jmeter安装部署2.1 Jmeter下载2.2 Jmeter安装2.3 Jmeter相关目录配置2.4 Jmeter启动配置2.5 检查并启动 3 Jmeter汉化3.1 临时修改3.2 永久修改 4 准备测…

【数据分析 | Numpy】Numpy模块系列指南(一),从设计架构说起

&#x1f935;‍♂️ 个人主页: AI_magician &#x1f4e1;主页地址&#xff1a; 作者简介&#xff1a;CSDN内容合伙人&#xff0c;全栈领域优质创作者。 &#x1f468;‍&#x1f4bb;景愿&#xff1a;旨在于能和更多的热爱计算机的伙伴一起成长&#xff01;&#xff01;&…

【开源视频联动物联网平台】视频接入网关的用法

视频接入网关是一种功能强大的视频网关设备&#xff0c;能够解决各种视频接入、视频输出、视频转码和视频融合等问题。它可以在应急指挥、智慧融合等项目中发挥重要作用&#xff0c;与各种系统进行对接&#xff0c;解决视频能力跨系统集成的难题。 很多视频接入网关在接入协议…

品牌如何用软文与用户产生联结?媒介盒子分享两大要素

做软文推广&#xff0c;重要的一点就是为别人提供有价值或有意思的内容&#xff0c;比如卖车的教汽车养护知识&#xff0c;做金融的分享理财知识&#xff0c;人家觉得在这里能找到想要的东西&#xff0c;自然会成为粉丝&#xff0c;效果比强行推销要好很多。但是许多企业在做软…

【计算机毕业设计】springboot+java高校实验室器材耗材料借用管理系统94l73

实验室耗材管理系统在Eclipse/idea环境中&#xff0c;使用Java语言进行编码&#xff0c;使用Mysql创建数据表保存本系统产生的数据。系统可以提供信息显示和相应服务&#xff0c;本系统教师和学生申请使用实验材料&#xff0c;管理员管理实验材料&#xff0c;审核实验材料的申请…

冒泡排序算法是对已知的数列进行从小到大的递增排序。

题目描述冒泡排序算法是对已知的数列进行从小到大的递增排序每个实例输出两行&#xff0c;第一行输出第1轮结果, 第二行输出最终结果 它的排序方法如下: 1.对数列从头开始扫描&#xff0c;比较两个相邻的元素,如果前者大于后者,则交换两者位置 2.重复步骤1&#xff0c;直到没有…

抖音本地生活服务商申请条件

抖音的本地生活服务商目前有两种&#xff0c;一种是可以做全国的服务商&#xff0c;我们一般叫抖音本地生活服务商&#xff0c;一种是区域优待服务商&#xff0c;也就是后面出来的服务商&#xff0c;这两种服务商的申请方式大同小异。 相同的地方就是都需要给平台交保证金。抖…

【深度学习实验】图像处理(三):PIL——自定义图像数据增强操作(随机遮挡、擦除、线性混合)

文章目录 一、实验介绍二、实验环境1. 配置虚拟环境2. 库版本介绍 三、实验内容0. 导入必要的库1. PIL基础操作2. Cutout&#xff08;遮挡&#xff09;2.1 原理2.2 实现2.3 效果展示 3. Random Erasing&#xff08;随机擦除&#xff09;3.1 原理3.2 实现3.3 效果展示 4. Mixup&…