C++ 11(2)右值引用 | 移动构造、移动赋值

news2024/11/26 3:48:31

前文中我们讲解了C++11中的部分知识点,下面我们来介绍一下C++11中的一个比较重要的知识点右值引用。

右值引用和移动语义

左值引用和右值引用

左值引用

左值就是一个数据的表达式(如变量名和解引用指针),我们可以获取它的地址+可以对其进行赋值,左值可以出现在赋值符号的左边也可以出现在赋值符号的右边,定义时const修饰符后的左值,不能给其进行赋值,但是可以取地址。左值引用就是给左值的引用,给左值取别名。

int main()
{
	// 以下的p、b、c、*p都是左值
	int* p = new int(0);
	int b = 1;
	const int c = 2;
	// 以下几个是对上面左值的左值引用
	int*& rp = p;
	int& rb = b;
	const int& rc = c;
	int& pvalue = *p;
	return 0;
}

右值引用

右值也是一个数据的表达式,如:字面常量、表达式返回值,函数返回值(这个返回值不是左值引用的返回)等等,右值可以出现在赋值符号的右边,但是不能出现在赋值符号的左边,且右值不能取地址。右值引用就是对右值的引用,给右值取别名。右值引用的符号就是&&。

int main()
{
	double x = 1.1, y = 2.2;
	// 以下几个都是常见的右值
	10;
	x + y;
	fmin(x, y);
	// 以下几个都是对右值的右值引用
	int&& rr1 = 10;
	double&& rr2 = x + y;
	double&& rr3 = fmin(x, y);
	// 这里编译会报错:error C2106: “=”: 左操作数必须为左值
	10 = 1;
	x + y = 1;
	fmin(x, y) = 1;
	return 0;
}

有一点需要注意的就是右值是不能取地址的,但是给右值取别名后,会导致右值被存储到特定位置,且可以取到该位置的地址,也就是说例如:不能取字面量10的地址,但是rr1引用后,可以对rr1取地址,也可以修改rr1。如果不想rr1被修改,可以用const int&& rr1 去引用。

int main()
{
	double x = 1.1, y = 2.2;
	int&& rr1 = 10;
	const double&& rr2 = x + y;
	rr1 = 20;
	rr2 = 5.5; // 报错
	return 0;
}

左值引用和右值引用比较

int main()
{
	int a = 0;
	int b = 1; 
	int* p = &a;
	a + b;
	// 左值引用给左值取别名
	int& ref1 = a;
	
	// 左值引用给右值取别名
	//int& ref2 = (a + b); err
	const int& ref = (a + b);

	// 右值引用给右值取别名
	int&& ref3 = (a + b);

	// 右值引用给左值取别名
	//int&& ref4 = a; err
	int&& ref4 = move(a);

	return 0;
}

右值引用总结:

1. 右值引用只能右值,不能引用左值。
2. 但是右值引用可以move以后的左值。

右值引用的使用场景和意义

首先我们来看一个简单的小例子:

void func(const int& a)
{
	cout << "void func(const int& a)" << endl;
}

void func(int& a)
{
	cout << "void func(int& a)" << endl;
}

void func(int&& a)
{
	cout << "void func(int&& a)" << endl;
}

int main()
{
	int a = 0; 
	int b = 1;

	func(a);
	func(a + b); // 如果只有左值引用,且临时变量具有常性,那么由于权限的放大是无法进行传递的;如果要进行传递就需要在func的形参中添加const。但是这样的无法对传入的参数是左值还是右值进行区分。有了右值引用就可以将左值与右值进行区分。
}

在上述的例子中就可以看出使用右值引用就可以对左值与右值进行区分。

第二个例子:

我们将右值可以进行区分将其分为纯右值与将亡值,例如,函数的返回值就可以视为将亡值。在我们自己编写的函数中编写了一个to_string的函数将整形字符变为string类型的数据。这个函数的返回值就是将亡值。

从上图中的内容符合我们之前学习过的知识,函数返回值进行传值返回的时候先要拷贝一份临时对象,然后临时对象再次给valStr进行一次拷贝构造,但是在某些编译器中就可以进行优化,被优化为一次的拷贝构造。

 最后得到的就是这样的显示:

那么在这里我们就会思考一个问题,是用函数的返回值进行拷贝构造传参还是有一些资源的浪费,毕竟函数的返回值属于出了函数的作用域就会销毁,我们是否可以将这返回值的数据直接移动到需要的位置没这样就可以减少拷贝构造的使用。于是在C++11中就产生了移动构造。

// 移动构造
string(string&& s)
	:_str(nullptr)
{
	cout << "string(string&& s) -- 移动拷贝" << endl;
	swap(s);
}

在string类中新编写一个新的构造函数参数为右值引用。这样在我们的设想中就会变成先拷贝构造临时对象,在使用临时对象进行移动构造。这样还是不太足够,毕竟还是又一个拷贝构造产生,编译器就会将先拷贝构造再移动构造的过程优化为直接进行移动构造的过程。由于函数return的值是一个左值,所以处理的时候编译器会想办法将这个返回值识别成右值,进行资源的转换,然后再对返回值进行销毁。

使用库中自带的string类型就可以看出s1的资源转移到了s3上。

同样在C++11中STL所有的容器都增加了右值引用的版本,并且所有容器的插入数据接口函数都增加了右值引用的版本。这样,例如在尾插一个右值的时候就可以直接进行资源的移动而不是进行深拷贝。

总结 

左值引用减少拷贝,提高效率,右值引用也是减少拷贝,提高效率,但是他们的角度不同,左值引用时直接减少拷贝,右值引用是间接减少拷贝,识别出是左值还是右值,如果是右值就不再进行深拷贝,直接移动拷贝提高效率。

完美转发

下面来看这样的一个问题:

我们可以将&&称之为万能引用,因为它不仅可以引用左值,还可以引用右值,我们有时会需要将一个函数传入的左值,再次向另一个函数传递就如下面的例子所示:

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(move(a)); // 右值

	const int b = 8;
	PerfectForward(b); // const 左值
	PerfectForward(move(b)); // const 右值
	return 0;
}

可以发现,不论我们传入的参数是什么样的形式最终都在Fun函数中显示的是左值。 原因是右值引用引用后属性会变为左值,这样才能够实现资源的转移。右值在被引用之后就会被存储到特定的位置,且可以取到该位置的地址,那么就可以对这个引用后的值进行修改或者资源转移等的操作。如果这个数在右值引用之后还是一个右值,那么后续对该值的一些列操作都可能会失败。那么为了能够进行正确的转发,就需要使用std::forward<T>(t),这个函数的作用就是完美转发在传参的过程中保留对象原生类型属性。

下面我们可以来看一个例子:

有一个自己编写的链表,使用自己编写的string类型作为变量类型对于下面的代码来说都执行的是深拷贝,这非常的好理解。

在代码中添加上&&的版本运行之后却发现并没有执行移动构造,这就是因为作为右值的变量push_back到链表中后它的属性就变为了左值,因此需要添加完美转发来保证变量的属性为右值。然而在push_back函数中添加完之后,结果并没有发生改变,因为这个函数还调用了insert函数,insert函数还调用了构造函数,形成了函数传递的过程。当确保了整个过程都是完美转发时候,就能够保证移动构造的执行。

void push_back(T&& x)
{
	insert(end(), forward<T>(x));
}

void insert(iterator pos, T&& x)
{
	node* cur = pos._node;
	node* prev = cur->_prev;

	node* new_node = new node(forward<T>(x));

	prev->_next = new_node;
	new_node->_prev = prev;
	new_node->_next = cur;
	cur->_prev = new_node;
}

list_node(T&& x = T())
	:_next(nullptr)
	, _prev(nullptr)
	, _data(forward<T>(x))
{}

移动赋值

除了移动构造在C++11中还有着移动赋值的出现,在没有右值引用之前,使用赋值重载的结果如下图,在to_string函数返回时调用了移动构造,如我们之前所述将函数返回值这个将亡值的资源进行转移,然后调用赋值重载在赋值重载中使用了现代写法进行深拷贝。

那么在有了右值引用之后就可以进行修改,将将亡值的资源直接移动拷贝到需要的地方。

// s = 将亡值
string& operator=(string&& s)
{
	cout << "string& operator=(string&& s) -- 移动拷贝" << endl;
	swap(s);

	return *this;
}

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

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

相关文章

JD 某工业平台详情API接口PHP接口数据API接口

随着电商市场的蓬勃发展&#xff0c;越来越多的企业正在寻找一种高效、便捷的方式来推销和销售自己的产品。在这个过程中&#xff0c;API接口的使用变得越来越普遍。而京东工业平台商品详情API接口正是一种在推销和销售方面迅速占据市场的技术。 京东工业平台商品详情API接口是…

百度富文本框对接

第一步:引入富文本插件 第二步:修改配置文件 1:ueditor.config.js中,var URL = window.UEDITOR_HOME_URL || getUEBasePath();改为window.UEDITOR_HOME_URL = "http://ip+端口/项目名"; var URL = "http://ip+端口/项目名/.../ueditor/1.4.3/"; …

CORS跨域资源共享漏洞复现——详细利用方法,漏洞危害最大化

文章目录 前言一、漏洞原理以及成因二、CORS漏洞利用总结 前言 在很久很久以前&#xff0c;2021年&#xff0c;我写过一篇关于CORS的漏洞文章&#xff0c;CORS跨域资源共享漏洞靶场演示。当时技术水平欠佳&#xff0c;而且文章内容也仅仅是简单过了一遍漏洞的原理和检测方法&a…

芯片设计全流程概述

芯片设计分为前端设计和后端设计&#xff0c;前端设计&#xff08;也称逻辑设计&#xff09;和后端设计&#xff08;也称物理设计&#xff09;并没有统一严格的界限&#xff0c;涉及到与工艺有关的设计就是后端设计。 1、规格制定 芯片规格&#xff0c;也就像功能列表一样&…

详解c++---AVL树的原理和实现

目录标题 搜索二叉树的缺点什么是AVL树平衡因子的变化规律AVL树的旋转准备工作insert函数模拟实现左旋转右旋转右左双旋左右双旋 AVL树的打印AVL的查找AVL树的检查 搜索二叉树的缺点 在上一篇文章的学习种我们知道了什么搜索二叉树&#xff0c;它让比根小的节点都在根的左边&a…

数字孪生水网可视化平台提高企业应急联动指挥水平

随着城市化进程的不断加快&#xff0c;给水管网的建设和维护变得越来越重要。传统的给水管网监测和管理方式通常只能通过文字和图片来进行描述和展示&#xff0c;难以直观地了解管网的结构和运行情况。而3D可视化大屏展示技术的出现&#xff0c;为给水管网的监测管控提供了以下…

可完成城市内涝一维二维耦合模拟的慧天[HTWATER]软件讲解

第一部分&#xff1a;CAD、GIS在水力建模过程中的应用 1.1复杂城市排水管网系统快速建模&#xff1a;通过标准化的步骤&#xff0c;利用CAD数据、GIS数据建立SWMM模型。在建模的不同阶段发挥不同软件的优势&#xff0c;实现高效的数据处理、准确的参数赋值、模型的快速建立。在…

【NPS 】1. 服务器端搭建

一、环境&#xff1a; 二、安装docker docker-compose 三、安装 nps 服务端 1. 在 /mnt/docker/nps 下创建 docker-compose.yaml 文件 2. 安装 nps 四、登录 1. ip:8800登录&#xff0c;默认 账号密码 admin / 123 2. 修改密码 一、环境&#xff1a; 系统 &#xff1a;C…

2023年6月中国数据库排行榜:OceanBase 连续七月踞榜首,华为阿里谋定快动占先机

群雄逐鹿&#xff0c;酣战墨坛。 2023年6月的 墨天轮中国数据库流行度排行 火热出炉&#xff0c;本月共有273个数据库参与排名。本月排行榜前十变动不大&#xff0c;可以用一句话概括为&#xff1a;OTO 组合连续两月开局&#xff0c;传统厂商GBase南大通用乘势而上&#xff0c;…

将深度学习与传统计算机视觉进行比较

原创 | 文 BFT机器人 深度学习 (DL) 在数字图像处理中用于解决难题&#xff08;例如&#xff0c;图像着色、分类、分割和检测&#xff09;。卷积神经网络 (CNN) 等深度学习方法通过使用大数据和丰富的计算资源提高预测性能&#xff0c;突破了可能性的界限。 深度学习是机器学习…

学习一年Java的程序员的C++学习记录(指针引用绕晕记)

文章目录 一 C入门二 变量和数据类型三 运算符四 流程控制五 复合数据类型六 函数七 函数高阶八 面向对象 一 C入门 标准输出流中 cout 是一个ostream对象&#xff0c;<< 和 >>是C中经过重载的运算符&#xff0c;配合cout和cin使用时表示流运算符。C中是如何重载运…

苹果头显Vision Pro深度解读1 下一个十年计算机行业的标杆

1 苹果Vision Pro是下一个十年计算机行业的标杆。 今天主要给大家说下今年WWDC发布的vision pro&#xff0c;以及后面很多期给大家做一些vision pro背后大量的技术的分享。我这次是从头到位把苹果官网上所有的文档&#xff0c;视频&#xff0c;全部学习了一遍。好几十个视频文…

Stable Diffusion web UI之X/Y/Z plot使用

一、安装环境配置 PASS CFG Scale配置的越高&#xff0c;SD生成的图会更贴用户提供的prompt来进行生成&#xff0c;AI的自由度会下降&#xff0c;生成人物的时候特别需要注意&#xff0c;对于手脚脸部&#xff0c;过高的值更容易造成过拟合还有画面崩坏。 二、X/Y/Z plot 使用…

民间最大的社区,倒闭了

看到一则不起眼的消息&#xff1a;天涯社区已经无法打开。 时代抛弃你的时候&#xff0c;都不说一声再见&#xff0c;现实就是这样残酷。 记得我读大学的时候&#xff0c;天涯社区是国内互联网行业中最具影响力的论坛之一&#xff0c;号称 " 全球华人网上家园 “。 当年&a…

Maven配置仓库

目录 Maven仓库介绍 Maven配置本地仓库 Maven配置中央仓库 Maven配置远程仓库 配置jdk Maven仓库介绍 当使用 Maven 构建项目时&#xff0c;有三种仓库起着重要作用&#xff1a; 本地仓库&#xff1a;就像你自己的书库。当你使用 Maven 下载依赖项时&#xff0c;它们会被…

某企业《IT治理管理办法》共十个章节,五十九条管理要求,适用于集团级企业、大型企业、中型企业

第二条&#xff1a;IT治理是规范公司各部门在公司IT系统建设、IT 应用中的责任与权力分配&#xff0c;主要包括原则、IT架构、基础设施、IT应用、IT投入等方面&#xff0c;明确责任人以及决策权力人&#xff0c;以提升工作效率&#xff0c;提高决策的科学性和合理性。 总则的第…

HarmonyOS元服务端云一体化开发快速入门(下)

四、关联云开发资源 为工程关联云开发所需的资源&#xff0c;即在DevEco Studio中选择您的华为开发者帐号加入的开发者团队&#xff0c;将该团队在AGC的同包名应用关联到当前工程。具体操作如下&#xff1a; 1.&#xff08;可选&#xff09;如您尚未登录DevEco Studio&#xf…

深入理解ASEMI代理光宝LTV-152光耦的特性与应用

编辑-Z 光耦LTV-152是一种广泛应用于电子设备中的光电器件&#xff0c;它的主要功能是实现电路之间的隔离和信号传输。本文将深入探讨光耦LTV-152的特性和应用&#xff0c;帮助读者更好地理解和使用这种重要的电子元件。 一、光耦LTV-152的特性 1. 高隔离电压&#xff1a;光耦…

日撸java三百行day63-65

文章目录 说明1. Day63-65 AdaBoosting算法1 AdaBoostin举例1.1数据样本1.2 举例过程 2. 理论知识3. 总结 2. 代码理解1. WeightedInstances类2. 选择基分类器并进行训练&#xff08;树桩分类器&#xff09;3. 计算误差率和误差系数&#xff08;树桩分类器&#xff09;4. 计算精…

谈谈Java高并发网站的设计思路

目前Java都在流行一个说词&#xff1a;高并发。 反正不管是不是&#xff0c;反正就是高并发。 谈高并发&#xff0c;我们需要知道几个名词&#xff1a; -响应时间(Response Time&#xff0c;RT)-吞吐量(Throughput)-每秒查询率QPS(Query Per Second)-每秒事务处理量TPS(Transa…