右值引用和移动语句(C++11)

左值引用和右值引用

回顾引用

我们之前就了解到了左值引用,首先我们要了解引用在编译器底层其实就是指针。具体来说,当声明引用时,编译器会在底层生成一个指针来表示引用,但在代码编写和使用时,我们可以像使用变量类似取别名的方式一样来操作引用,而不需要显式地使用指针符号。这使得引用更为方便,且看起来更直观,同时也能保证所获得的引用总是有效的。那什么是左值,什么又是右值呢??

 

什么是左值与右值

左值是表示数据的表达式(如变量名或解引用的指针),也可能在赋值符号右边,具有地址的、可寻址的表达式才是左值。(typename & )

右值是一个表达式,如:字面常量、表达式返回值,函数返回值等等,且右值不能出现在赋值符号左边不能进行取地址才是右值。(typename &&)

 

 右值、左值引用的使用

//左值引用右值
int main()
{
	double x = 1.1, y = 2.2;
	double& r1 = x;
	//double& r2 = x + y;//此时的x1+x2是右值,必须加const修饰才能引用
	const double& r2 = x + y;

	return 0;
}

1. 左值引用只能引用左值,不能引用右值。
2. 但是const左值引用既可引用左值,也可引用右值。


 

//右值引用左值
int main()
{
	double x = 1.1, y = 2.2;
	double&& rr1 = x+y;
	//double&& r2 = x;//此时的x1是左值,所以要将属性转换再引用
	double&& r2 = move(x);

	return 0;
}

1. 右值引用只能右值,不能引用左值。
2. 但是右值引用可以move以后的左值。(此时改变的其实是返回值属性


移动构造和移动赋值

引用的好处就是不需要进行拷贝,所以对于自定义类型而言,引用的好处就显得更为重要。那么右值引用的好处也是同样......

下面就简单的以my_string的构造相关函数进行举例:

class my_string
{
public:
	//构造函数
	my_string(const char* tmp="")
		:_size(strlen(tmp))
		, _capacity(_size)
	{
		_s = new char[_capacity + 1];//还有一个\0
		strcpy(_s, tmp);
	}
	// 拷贝构造
	my_string(const my_string& s)
		:_s(nullptr)
	{
		cout << "my_string(const my_string& s) -- 深拷贝" << endl;
	}
	// 赋值重载
	my_string& operator=(const my_string& s)
	{
		cout << "my_string& operator=(my_string s) -- 深拷贝" << endl;
	}

private:
	char* _s;
	size_t _size;
	size_t _capacity;
};

以to_string函数举例深拷贝的消耗: 

my_string to_string(int num)
{
	my_string ret;
	//.........
    //对ret操作
	return ret;//ret返回值会拷贝构造临时对象
}
int main()
{
	my_string s;
	s = to_string(1234);//调用赋值重载

}

其实经过分析我们知道,to_string函数内部创建了一个对象,所以在函数调用结束的时候就会释放该ret对象的空间,所以当我们返回的时候实质上是将ret存进一个临时对象中作为返回值,所以此时会发生第一次的深拷贝,最后返回值再赋值给s对象,所以又发生了一次深拷贝。此时就相当于一共复制拷贝了两份空间,再算上原来的就有三份空间,所以此时如果该空间不是存的to_string,而是存的是其他的大量数据的话,那么此时的消耗是极其大的。

此时其实我们要了解C++11中将右值分为两类:1.纯右值(内置类型的右值)2.将亡值(自定义类型的右值) 

而此时我们的ret出了作用域就销毁,而且还会创建临时对象,所以我们的ret其实会被视作将亡值,所以此时就可以看作是一个右值(编译器进行的特殊处理),所以我们就可以不用再调用拷贝构造来创建临时对象了呀,反正该对象也要销毁,何不直接将临时对象指向该将亡值呢。同理再进行赋值的时候同样也可以啊(函数返回值就是典型的右值,临时对象)

 所以就引出了移动构造和移动拷贝:

    void swap(my_string& s)
	{
		std::swap(_s, s._s);
		std::swap(_size, s._size);
		std::swap(_capacity, s._capacity);
	}
	// 移动构造
	my_string(my_string&& s)//此时编译器会选择更匹配的而不是const my_string&
		:_s(nullptr)
		, _size(0)
		, _capacity(0)
	{
		swap(s);
	}
	// 移动赋值
	my_string& operator=(my_string&& s)
	{
		swap(s);
		return *this;
	}

但是我们要注意的点就是,当我们实现了移动拷贝和移动赋值以后,那么我们将对象move修饰之后进行拷贝和赋值以后,会进行swap进行资源的转移,所以会将该move对象的资源交换,所以在进行移动拷贝之后,该move对象的资源交换后就没了,而调用移动赋值之后,move对象的资源就是交换了。

所以最好不要随意地对一个左值进行move修饰,可能会有意想不到的结果发生。 


其实C++11之后,对于容器的push函数都增加了移动构造的形式,这种尤其是对那些需要进行深拷贝的对象而言会方便很多。此时就不需要再拷贝一份,直接将资源交换即可完成拷贝工作。

 右值引用后的属性

  右值被右值引用之后的属性是左值所以就可以被修改:

int main()
{
    double x = 1.1, y = 2.2;
    int&& rr1 = 10;
    const double&& rr2 = x + y;
    rr1 = 20;//右值引用后可以修改
    rr2 = 5.5; // 报错

    return 0;
}

实例: 

 就该移动构造而言,s对象就是右值引用的过后的,再调用swap函数,此时在string&& s接受参数的时候其实s对象的属性就变成了左值,即可被修改,所以传给swap函数时用string& s接受就没问题,但是两个过程中自始至终依旧是一个对象,没有创建额外的对象。

 

就拿这上述string的push_back函数来说,我们知道C++11之后,容器的push_back函数都重载了一份右值插入。所以对于以上的函数来说就显得很麻烦了,因为我们知道右值引用接收右值以后,数据的属性就会变成左值,因此对于这种函数嵌套调用的情况就会显得十分繁琐,需要将每次参数都move一下才可以实现接下来的右值引用接收。但是我们move之后就相当于是写固定了,所以相较于每一次的move对象,我们用完美转发会更好。

 

完美转发 


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);//调用fun函数时并不清楚t对象的类型
}

std::forward 完美转发在传参的过程中保留对象原生类型属性

func( forward<T>(t) )//完美转发,保持原有属性

 新的类功能

其实我们的移动构造和移动赋值函数也会默认生成,但是条件是:类中没有实现析构函数 、拷贝构造、拷贝赋值重载(就是都没有实现才满足条件)对于默认生成的移动构造函数,对于内置类
型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。
 

强制生成默认函数的关键字default
禁止生成默认函数的关键字delete

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

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

相关文章

面试题:MySQL为什么选择B+树作为索引结构

文章目录 前言二、平衡二叉树(AVL)&#xff1a;旋转耗时三、红黑树&#xff1a;树太高四、B树&#xff1a;为磁盘而生五、B树六、感受B树的威力七、总结 前言 在MySQL中&#xff0c;无论是Innodb还是MyIsam&#xff0c;都使用了B树作索引结构(这里不考虑hash等其他索引)。本文…

一缕青丝寄相思

10年8月16日七夕节男孩向女孩表白,女孩不知道那天是七夕,也没有读懂男孩的爱,女孩在9月22日中秋,向男孩打开了心门,男孩却没有懂女孩的心思.13年后的一封问候邮件,一束女孩的长发和回不去的青春 洒满阳光的午后 转眼间看到你的笑脸 微笑着你对我说 遇上你认识我真好 你说得好莫…

数据结构与算法(Java) -单调队列单调栈题单

单调队列&#xff08;灵神笔记&#xff09; 239 滑动窗口最大值 239. 滑动窗口最大值 - 力扣&#xff08;LeetCode&#xff09; 给你一个整数数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗…

找不到DNS地址的解决方案

找不到DNS地址的解决方案 第一种解决方案&#xff1a;刷新DNS缓存第二种解决方案&#xff1a; 配置Internet协议版本4&#xff08;TCP/IPv4&#xff09;配置IP地址配置DNS地址 如何查看本机IPv4地址、子网掩码与默认网关 第一种解决方案&#xff1a;刷新DNS缓存 WINR输入cmd回…

对比ProtoBuf和JSON的序列化和反序列化能力

1.序列化能力对比验证 在这里让我们分别使用PB与JSON的序列化与反序列化能力&#xff0c;对值完全相同的一份结构化数据进行不同次数的性能测试。 为了可读性&#xff0c;下面这一份文本使用JSON格式展示了需要被进行测试的结构化数据内容: {"age" : 20,"name…

2023年第十二届数学建模国际赛小美赛A题太阳黑子预测求解分析

2023年第十二届数学建模国际赛小美赛 A题 太阳黑子预测 原题再现&#xff1a; 太阳黑子是太阳光球上的一种现象&#xff0c;表现为比周围区域暗的暂时斑点。它们是由抑制对流的磁通量浓度引起的表面温度降低区域。太阳黑子出现在活跃区域内&#xff0c;通常成对出现&#xff…

Android自定义View实现八大行星绕太阳转动效果

最近尝试使用Android自定义View实现了一个8大行星绕太阳转动的自定义View效果&#xff0c;效果静态图如下所示&#xff1a; 还没来得及对该效果进行比较通用的包装&#xff0c;仅仅实现效果&#xff0c;有兴趣的可以继续扩展、美化、包装一下。 核心代码就一个类PlanetsView。 …

js中setinterval怎么用?怎么才能让setinterval停下来?

setinterval()是定时调用的函数&#xff0c;可按照指定的周期&#xff08;以毫秒计&#xff09;来调用函数或计算表达式。 setinterval()的作用是在播放动画的时&#xff0c;每隔一定时间就调用函数&#xff0c;方法或对象。 setInterval() 方法会不停地调用函数&#xff0c;…

Stm32F401RCT6内部FLASH数据擦除读写方法

Stm32F401RCT6内部FLASH数据的分区和F103的已经不一样了&#xff0c;读写格式化的方法网上内容不多&#xff0c;自己摸索了一下&#xff0c;基本可以&#xff0c;还存在一个问题 读取&#xff1a; uint16_t f[5];uint8_t tx[10];f[0] *(volatile uint16_t*)0x08020000; //ST…

tar文件覆盖漏洞 CVE-2007-4559

文章目录 前言原理例题 [NSSRound#7 Team]新的博客方法一 手搓文件名方法二 python脚本 前言 做到[NSSRound#6 Team]check(Revenge)时发现是tar文件覆盖&#xff0c;但是对概念和执行过程理解不够深就光光记住脚本&#xff0c;所以在做本题[NSSRound#7 Team]新的博客时打算重新…

一个网站,四种创建制作电子期刊的方法

想象一下&#xff0c;你正在走进一家神奇的商店&#xff0c;里面陈列着各种精美的杂志和期刊。但是&#xff0c;这些杂志和期刊并不是印刷品&#xff0c;而是可以直接在网站上制作和发布的电子期刊。 但是像这样能在网上发的电子期刊该怎么制作呢&#xff1f;不知道如何制作的小…

cc-product-waterfall仿天猫、淘宝购物车店铺商品列表组件

cc-product-waterfall仿天猫、淘宝购物车店铺商品列表组件 引言 在电商应用中&#xff0c;购物车体验的优化对于提升用户满意度和转化率至关重要。在本文中&#xff0c;我们将深入探讨如何使用cc-product-waterfall组件&#xff0c;结合uni-number-box和xg-widget&#xff0c;…

『Nginx安全访问控制』利用Nginx实现账号密码认证登录的最佳实践

&#x1f4e3;读完这篇文章里你能收获到 如何创建用户账号和密码文件&#xff0c;并生成加密密码配置Nginx的认证模块&#xff0c;实现基于账号密码的登录验证 文章目录 一、创建账号密码文件1. 安装htpasswd工具1.1 CentOS1.2 Ubuntu 二、配置Nginx三、重启Nginx 在Web应用程…

Linux驱动开发学习笔记1《字符设备驱动开发》

目录 一、字符设备驱动简介 二、chrdevbase 字符设备驱动开发实验 1.创建驱动程序的目录 2.创建vscode工程 3.编写实验程序 4.编译驱动程序和测试APP代码 &#xff08;1&#xff09;加载驱动模块 &#xff08;2&#xff09;创建设备节点文件 &#xff08;3&#xff…

【开源】前后端分离的在线考试系统,支持多种部署方式

在线考试系统 https://download.csdn.net/download/mo3408/88593116 在线考试系统是一种利用网络技术&#xff0c;实现在线出题、答题、阅卷、成绩查询等一系列考试活动的系统。它不受地理位置限制&#xff0c;可以实现远程考试&#xff0c;大大提高了考试的效率和便利性。此…

HBASE命令行查看中文字符

问题记录 中文显示的是编码字符不方便查看value\xE5\xB8\xB8\xE5\xAE\x89\xE5\xAE\x891修改前中文显示&#xff1a; 解决方法 1、列族 : 列名 : toString ’2、列族 : 列名 : c(org.apache.hadoop.hbase.util.Bytes).toString ’ scan karry:student,{COLUMNS > [info:…

C-语言每日刷题

目录 [蓝桥杯 2015 省 A] 饮料换购 题目描述 输入格式 输出格式 输入输出样例 # [蓝桥杯 2023 省 A] 平方差 题目描述 输入格式 输出格式 输入输出样例 说明/提示 【样例说明】 [NOIP2001 普及组] 数的计算 题目描述 输入格式 输出格式 输入输出样例 说明/提示 样例 1 解释 数据…

SQL自学通之简介

目录 一、SQL 简史 二、数据库简史 1、Dr. Codds 对关系型数据库系统的十二条规则 2、设计数据库的结构 3、数据库的前景 4、对于什么是客户机/服务器型电脑系统 BernardH.Boar的定义如下&#xff1a; 5、交互式语言 6、易于实现 7、SQL 总览 三、流行的 SQL 开发工具…

python初始化矩阵相关

做算法题经常需要初始化一个二维的dp数组 下面两种方法是最常用的 matrix [[0]*n]*n matrix [[0]*n for _ in range(n)]以前经常混用也没发现什么问题&#xff0c;直到昨天debug的时候发现第一种初始化之后对矩阵进行赋值时混乱的&#xff0c;比如matrix[0][1]2会导致所有行…

[Linux ] sed文本处理和免交互

一、sed 1.1 sed是什么 sed 是一种流编辑器&#xff08;stream editor&#xff09;&#xff0c;用于对文本数据进行文本转换和处理。它通常被用于在命令行中执行文本编辑任务&#xff0c;可以对输入的文本进行搜索、替换、删除等操作&#xff0c;并将结果输出。sed 是一个非交…