c++11标准(1)右值引用和移动构造

news2024/11/15 11:13:51

欢迎来到博主的专栏:c++杂谈
博主ID:代码小豪

文章目录

    • 左值和左值引用
    • 右值和右值引用
      • 右值
      • 右值引用
    • 右值引用的使用场景与意义

右值引用是c++11标准推出的新特性,在此之前,引用都是左值引用。为了弄清楚什么是右值引用,首先我们要知道什么是右值

左值和左值引用

左值是c语言就有的概念。在c++中,左值通常是一个变量,一个指针,对象,亦或者是一个函数的引用返回值。

int a;//变量
const int b=0;
int* p = &a;//指针
std::string str;//对象
str[0];//函数返回的引用

对于左值,左值引用便是左值的引用类型,左值引用只能引用左值。

//左值引用
int& ra = a;//变量的引用
const int& rb = b;
int*& rp = p;//指针的引用
std::string& rstr = str;//对象的引用
char& rch = str[0];//函数返回值的引用

那么关于左值,有没有更好的区分方法?答案是有的,一切的左值都能取地址。比如:

int* pa = &a;
int** ppa = &pa;
std::string* pstr = &str;
char* pch = &str[0];

右值和右值引用

右值

右值也是C语言时期提出来的概念了,可能有人会说了,赋值操作符(=)右边的值都是右值,左边的都是左值,这并不难区分。但是事实是右值是赋值操作符(=)右边的值,但是赋值操作符右边的值可以是左值。因此赋值操作符右边的值不一定都是右值。

在c++当中,右值通常是一个字面常量(常量并非都是右值,比如const修饰的变量就是左值,只有字面常量则是右值),函数传值返回的返回值,类型转换产生的临时变量,以及匿名对象。

int x = 10, y = 20;
10;//字面常量
x + y;//算数表达式的结果
fmin(x,y);//标准库的函数,其返回值是double类型,传值返回
(double)x;//类型转换产生的临时变量
std::string("11111");//匿名对象

左值引用不能引用右值。但是const修饰的左值引用可以引用右值。

int& r1 = 10;//error
double& r2 = (double)x;//error
std::string &= std::string("11111");//error
double& rd = fmin(x, y);//error

const int& r1 = 10;//ok
const double& r2 = (double)x;//ok
const std::string &r3= std::string("11111");//ok
const double& rd = fmin(x, y);//ok

那既然const修饰的左值引用可以引用右值,那为什么还要设计一个右值引用呢?首先,博主先给出一个结论。const修饰的左值引用!=右值引用。

要知道右值引用是c++11才推出的特性,而左值引用在c++设计之初就有的东西。因此右值引用推出的目的并非单纯的是引用右值。

说的有点远了,之所以const修饰的左值引用可以引用右值,是因为有些函数的参数会写成左值引用类型,因为这可以提高程序的运行效率。比如vector的push_back函数。

void push_back (const value_type& val);

c++的设计这希望这些以左值引用传参的函数,也能用右值来调用。因此允许const修饰的左值引用可以引用右值。

右值引用

右值引用就是在类型生命之后加上(&&),这表示是某个类型的右值引用。

int&& rr1 = 10;//ok,rr的意思是right reference(右值引用的缩写)
int&& rr2 = x + y;//ok
int&& rr3 = (double)x;//ok
std::string&& rr4 = std::string("1111");//ok

和左值引用一样,右值引用不允许引用左值。

int&& rrx = x;//error 右值引用不能引用左值
std::string&& rrstr = str;//error 右值引用不能引用左值
double&& rrd = fmin(x, y);//ok

但是标准库存在一个函数move(),能让左值变成右值引用的类型。为什么要这样做呢?博主在后面再谈吧。

std::string&& rrstr = move(str);//ok
int&& rrx = move(x);//ok

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

首先我们要明白一点,右值不能取地址,不代表右值没有地址。在计算机硬件层面上讲,所有的数据都是二进制,竟然右值可以与左值一起参与计算,那么在硬件当中,右值一定也有其对应的二进制存储,所以右值不能取地址是语法方面的限制,而非右值不存在地址。

既然右值可以有地址,那么右值引用的作用给这个右值的地址起一个别名,使得右值可以被利用。那么问题来了,什么情况下需要用到右值引用呢?

无论是左值引用,还是右值引用,其核心目的只有一个,即减少拷贝。既然c++11推出了右值引用,那么右值引用一定解决了一些左值引用无法解决的情况。因此在此之间,我们先来看看左值引用的缺点是什么。

我们以string类为例(这里的string类是博主自己写的,因为使用标准库的string体现的不够明显)。该string类的拷贝构造函数和operator=函数如下:

mystring::mystring(const mystring& str)
{
	mystring tmp(str._str);//深拷贝
	swap(tmp);
}

mystring& mystring::operator=(mystring s)//参数s会导致深拷贝
{
	swap(s);
	return *this;
}

假如我们调用这个函数:

mystring makestring(const char* str)
{
	mystring str(str);
	return str;
}

int main()
{
	mystring str = makestring("hello world");
	return 0;
}

在这里插入图片描述
从上图的示意图可以看出,首先,由于返回值是str的拷贝,因此会调用以此拷贝构造函数,然后又会调用operator=进行以此拷贝,而string对象是一个需要深拷贝的类,因此这两次拷贝导致的时间开销特别大,如果将返回值改成string&(左值引用),那么又会因为str超出了生命周期,导致对象被销毁,数据不能成功被拷贝。

那么这也是左值引用不能解决的麻烦之一,那就是对传值返回的函数的优化还不够大。为什么这么说呢?

我们思考一个问题,那就是右值和左值的的区别是什么?或者说左值和右值的特性有什么区别?ok。右值和左值最大的区别在于,右值的生命周期比左值少多了,比如对于局部变量来说,其生命周期是函数的调用堆栈,或者for,if等语句的语句块,对于堆区的变量来说,其生命周期则是从new出来到delete掉该变量的整个区间。而右值的生命周期则仅仅只存在一行语句当中。换句话说,左值的生命周期远比右值要长。

因此对于左值引用,我们的程序就要采取对待左值的策略:

由于这个左值会在程序当中存在很长一段时间,因此如果拷贝一个左值(如拷贝构造。operator=),我们不会去做可能损毁这个左值的操作,因为我们不能确保这个被损坏的左值不会在后续的程序当中使用,如果该左值在后续的程序当中被使用了,那么造成的后果轻则逻辑错误,重则程序崩溃。

而右值由于其生命周期很短,后续的程序当中其不会再参与计算。所以右值对于我们而言,还有一个隐藏的价值,那就是右值当中存储的数据。比如我们可以设计一个针对右值的构造函数

mystring::mystring(mystring&& str)
{
	char* tmp = str._str;
	str._str = _str;
	_str = tmp;
	_capacity = str._capacity;
	_size = str._size;
}

由于str是即将销毁的右值,出了表达式mystring str = makestring("hello world");就要调用析构函数销毁的对象,也就是说str的生命周期只存在于短短一行代码当中。那么本着白用白不用,用了也白用,白用谁不用的态度。那么我们来一手卸磨杀驴吧,既然你str的生命短暂,将str进行魔改也不会影响后续的程序。于是这个右值引用的构造函数就来了一个很大胆的操作。
在这里插入图片描述
既然右值即将销毁,其当中存储的数据也要随之销毁,那么还不如将存储的数据直接给左值吧。左值会带着你的数据继续活下去的,这样做的好处在于,用移动数据来替代深拷贝,可以节省下大量的时间开销,提升程序的运行速率。这种操作称为移动。关于移动构造和移动赋值都是c++11推出的产物,与移动相关的函数博主放在其他文章。

拷贝左值引用的情况,是不敢这么做的,因为拷贝一个左值要考虑到这个左值还有很长的生命周期,我们要考虑到这个没有数据的左值会对程序造成什么样的破坏。因此拷贝左值引用是采取深拷贝的策略。
在这里插入图片描述
这也就是右值引用的价值所在了,既然右值命不久矣,那么不如直接以移动代替拷贝,以达到节省时间的目的。而对于某个不想再用的左值,则可以通过move函数来让这个左值发生移动构造。

ly::mystring str1 = makestring("hello world");
ly::mystring str2 = move(str1);//将str1强制转换为右值引用类型

因为程序员可以在控制某些左值在后面阶段不再使用,因此move()函数的作用实际上是让程序员决定一些左值的生杀大权,因为一旦左值被视作了右值引用,那么就会触发移动构造,将这个左值的数据给掏空,但是注意这个左值的生命周期不会被改变,因此被move过的左值就要注意不要在后续的程序当中被使用了。

那么我们最后在回到这个函数

mystring makestring(const char* str)
{
	mystring str(str);
	return str;
}

int main()
{
	mystring str = makestring("hello world");
	return 0;
}

由于存在了移动构造,因此str不需要再深拷贝makestring的返回值了,因为这个函数的返回值是一个右值,因此直接调用移动构造,节省了深拷贝的时间开销。
在这里插入图片描述
由于对右值进行利用可以提高程序的运行效率,因此STL中的大量函数也新增了大量右值引用的代码,比如序列式容器中的push_back,关联式容器中的insert,以及容器的move构造。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

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

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

相关文章

Euro 2024 足球中的IMU技术突破

在体育技术领域,IMU(惯性测量单元)技术正以前所未有的方式重塑足球比赛。Adidas Fussballliebe Finale足球,作为首个在欧洲锦标赛中采用公司“连接球技术”的官方比赛用球,展示了IMU技术在现代足球中的应用。以下是这款…

『.NET 高手必备』深入浅出RulesEngine:业务逻辑抽象新利器!

亲爱的朋友们,我是许泽宇,今天我要和大家探讨一款强大的业务规则库——Microsoft RulesEngine。在软件开发中,业务逻辑的变更和扩展往往如同“潘多拉魔盒”,一旦打开就难以收拾。RulesEngine 的设计理念就是为了解决这个难题&…

从“治理”到“智理”,看大模型如何赋能智慧政务

一、从治理到智理的飞跃 在智慧城市的建设蓝图中,智慧政务如同一股不可忽视的力量,正悄然改变着城市的治理面貌。传统意义上,“治理”往往意味着对复杂社会现象的被动应对,而“智理”则预示着通过智能化手段主动预见、解决问题的…

最优化理论与自动驾驶(一):概述

目录 1. 最优化理论的原理 2. 最优化问题的分类 1. 按目标函数的性质分类 2. 按变量的性质分类 3. 按约束条件分类 4. 按时间维度分类 5. 按不确定性分类 6. 按决策变量的维度分类 3. 常用的最优化方法 1. 梯度类优化算法 2. 约束优化算法 3. 启发式算法 4. 线性规…

Internet选项检查所存网页的较新版本的设置

每次访问此页时检查代表着,你无论打开任何网页时,都不用IE缓存,直接刷新浏览每次启动IE时检查,代表着,只要你IE浏览器不关闭,,那么他在访问相当网站,网页时,就会调用IE缓存(你会感觉打开非常快),这时候,他并没有下载网页,,只是调用缓存而已。。但是如果你关闭IE浏览器,再重新打…

考软考的信息安全工程师,有什么诀窍在一个月内通过吗?

一般是至少是2个月时间拿来备考的,低于2个月的话,时间肯定是比较赶的。虽然一个月时间相对紧张,但通过合理规划和高效利用时间,也是有可能成功通过考试的。以下是一份详细的备考策略,旨在帮助大家在有限的时间内最大化…

springboot家政服务管理系统—计算机毕业设计源码34242

摘 要 科技进步的飞速发展引起人们日常生活的巨大变化,电子信息技术的飞速发展使得电子信息技术的各个领域的应用水平得到普及和应用。信息时代的到来已成为不可阻挡的时尚潮流,人类发展的历史正进入一个新时代。在现实运用中,应用软件的工作…

数据结构与算法——顺序表期末复习五大经典题型

目录 一:顺序表-移除元素 二:顺序表-删除有序数组中的重复项 三:顺序表-合并两个有序数组 四:顺序表-旋转数组 五:顺序表-数组形式的整数加法 一:顺序表-移除元素 题型链接:27. 移除元素 -…

【期末复习】大二下系列 公共课专业课 复习笔记合集

计算机网络 老师划重点 第一章 计算机网络概述 1.3 互联网概述(概念,因特网四个阶段) 因特网四个阶段: 因特网草案建议标准草案标准因特网标准 1.5 计算机网络的主要性能指标 1.6 计算机网络体系结构(三种划分和各…

黑神话悟空mac可以玩吗

黑神话悟空mac上能不能玩对于苹果玩家来说很重要,那么黑神话悟空mac可以玩吗?目前是玩不了了,没有针对ios系统的版本,只能之后在云平台上找找了,大家可以再观望下看看。 黑神话悟空mac可以玩吗 ‌使用CrossOver‌&…

cdr怎么画虚线?

虚线是以点或者短线画成的断续的线,多用于几何图形或者标记。我们在平常学习工作中经常使用标记的方式。根据国标制图规定 机械制图 图样画法 图线规定,技术图样中,主要使用细虚线和粗虚线两种,细虚线用以表示不可见棱边线和不可见…

Feed流系统重构:架构篇

重构对我而言,最大的乐趣在于解决问题。我曾参与一个C#彩票算奖系统的重构,那时系统常因超时引发用户投诉。接手任务时,我既激动又紧张,连续两天几乎废寝忘食地编码。结果令人振奋,算奖时间从一小时大幅缩短至十分钟。…

半导体行业跨国文件传输解决方案

在数字化浪潮席卷全球的今天,半导体行业无疑是科技创新的先锋。随着对数据传输需求的激增,传统的文件传输协议FTP在处理半导体行业庞大的文件量和复杂的文件结构时显得捉襟见肘。尤其是在跨国文件传输场景中,传统方法已经难以跟上行业的发展步…

你了解什么是场外期权吗?

今天期权懂带你了解你了解什么是场外期权吗?场外期权是指在交易所之外进行交易的期权合约。这类期权通常是由买卖双方通过私人协议进行交易,而不是在标准化的交易所上进行。 场外期权的特点 1.定制化:场外期权合约可以根据交易双方的具体需…

AI写作神器:助力体制内小白轻松完成材料撰写,减少慌张茫然

刚刚进入体制内的工作者,对于公文材料撰写基本无从下手,力不从心,提交稿件后,反馈的问题大多存在结构格式不对、文字口语化,缺少理论依据等,笔者是做互联网的,所以对AI工具比较了解,…

FPGA在AI时代的应用与挑战:《详解FPGA:人工智能时代的驱动引擎》(可下载)

在当今这个信息爆炸的时代,人工智能(AI)和大数据已经成为了推动技术进步和产业革新的核心力量。在这一浪潮中,FPGA作为一种极具灵活性和强大计算能力的半导体器件,正扮演着越来越重要的角色。 本片文章将为你带来书籍…

战神诸神黄昏9月19日登录PC端! 手机怎么玩战神诸神黄昏

9月19日,《战神:诸神黄昏》正式登录PC端,这是一部动作冒险游戏。要是你想随时随地在手机或平板上也能玩《战神:诸神黄昏》,可以使用网易GameViewer远程帮你实现。 网易GameViewer远程作为一款专为游戏玩家打造的远程软…

智能硬件从零开始的设计生产流程

文章目录 市场分析团队组建ID设计结构设计pcba设计软件开发手板EVT开模DVTPVTMP 智能硬件研发是一个复杂的过程, 当然一件事要发出萌芽必须得有人, 有一天,几个合伙人凑在一起,说一起开发个智能硬件产品吧,于是故事开始了. 市场分析 合伙人: 万物互联的时代, 智能音箱已经成为…

【LeetCode每日一题】2024年9月第二周(下)

2024.9.13 困难 难度评分1917 链接:2398. 预算内的最多机器人数目 (1)题目描述: (2)示例 (3)分析 翻译一下题目:要求我们在给定的 chargeTimes 和 runningCosts 数组以…

ELFK日志分析平台,架构和通信

整个架构,加上跳板机,总共12台机器 技术方案: 1. 配置nfs服务器,为web集群提供共享网络文件系统 # 部署 NFS 服务 [rootnfs ~]# dnf install -y nfs-utils [rootnfs ~]# vim /etc/exports /var/webroot 192.168.1.0/24(rw,…