C++ 右值引用 和 移动构造函数

一: 背景

最近在看 C++ 的右值引用和移动构造函数,感觉这东西一时半会还挺难理解的,可能是没踩过这方面的坑,所以没有那么大的深有体会,不管怎么说,这一篇我试着聊一下。

二: 右值引用

1. 它到底解决了什么问题?

在其他编程语言中,很少听到 右值引用 这个词,我个人感觉还是 C++ 这个 值类型 优先的语言基因决定的,我们都知道 值类型 作为方法参数或者返回值时会生成自身的副本,如果 值类型 很大,那一来一回生成若干个深复制的 临时对象 将会产生巨大的性能开销。

总结一句话:右值引用 就是尽可能的减少这中间 临时对象 个数,尤其是关联到 heap 上的对象,仅此而已。

2. 右值引用是个什么样子?

说到 右值引用 得先说什么是 右值,左值 , 左值 一般都是带有内存地址的变量,而 右值 一般是立即数或者运算过程中的临时对象,这种对象不会有地址值,是不是很绕,我举个例子吧。


int main()
{
	int i = 10;
	int j = 11;

	int sum = i + j;
}

  1. 10,11,(i+j)

属于右值,因为它本身没有内存地址,除非把它们放入到栈中或者堆中。

  1. i,j,sum

属于左值,因为它们是线程栈上地址的标识符。

知道了 左右值 概念,接下来理解 左右值引用 就很简单了,既然是 引用,必然是多个变量指向同一个地址,对吧,修改下代码如下:


int main()
{
	int i = 10;
	int& k = i;		//左值引用


	int&& m = 10;	//右值引用
}

接下来看下汇编代码:


    33: 	int i = 10;
00FB182F  mov         dword ptr [ebp-0Ch],0Ah  
    34: 	int& k = i;	
00FB182F  mov         dword ptr [ebp-0Ch],0Ah  
00FB1836  lea         eax,[ebp-0Ch]  
00FB1839  mov         dword ptr [ebp-18h],eax  
    36: 	int&& m = 10;	
00FB183C  mov         dword ptr [ebp-30h],0Ah  
00FB1843  lea         eax,[ebp-30h]  
00FB1846  mov         dword ptr [ebp-24h],eax 

从汇编代码看,它们是一模一样的,也就是说在汇编层面,其实并没有 右值引用 和 左值引用 一说。

有了这些基础,我们来看下更复杂的 class 结构。

三: 右值引用如何减少对象的创建

1. 简要思路

其实仔细想一想,减少临时对象的创建,无非就是在运算过程中复用一些对象,不需要每次都走赋值构造函数来进行深复制,画个图就像下面这样。

聊聊 C++ 右值引用 和 移动构造函数

明白了这个思路,接下来我们举一个例子说明。

2. 一个简单的例子

C++ 最烦的地方就是有太多的构造函数, 数不胜数,太尴尬了,这里我做一个简单的 + 操作例子。



#include <iostream>
#include <vector>

using namespace std;

class StringBuidler {
public:
	char* str;
	int length;
public:
	StringBuidler() {}
	StringBuidler(int len, char c) {
		this->str = new char[len];
		this->str[0] = c;
		this->length = len;
	}

	StringBuidler(const StringBuidler& s) {

		printf("StringBuidler:深复制 \n");
		this->length = s.length;
		this->str = new char[s.length];

		for (size_t i = 0; i < length; i++)
		{
			this->str[i] = s.str[i];
		}
	}

	StringBuidler operator+(const StringBuidler& p) {

		StringBuidler tmp;

		tmp.length = this->length + p.length;
		tmp.str = new char[tmp.length];

		int index = 0;

		for (size_t i = 0; i < this->length; i++)
		{
			tmp.str[index++] = this->str[i];
		}
		for (size_t i = 0; i < p.length; i++)
		{
			tmp.str[index++] = p.str[i];
		}

		return tmp;
	}
};

int main()
{
	StringBuidler s1(10, 'a');
	StringBuidler s2(5, 'b');

	StringBuidler s3 = s1 + s2;

	printf("s3.length=%d, s1.length=%d, s2.length=%d \n", s3.length, s1.length, s2.length);
}

折叠 
聊聊 C++ 右值引用 和 移动构造函数

从这个例子中可以看到,s1+s2 操作中出现了一次 深copy,具体代码出现在 return 处,汇编代码如下:

聊聊 C++ 右值引用 和 移动构造函数

因为是深复制,所以会再次生成一个 new char[] ,如果 new char[] 很大,那将会是不必要的性能开销,能不能像我画的图一样,将 s3 中的 str 指针直接指向 tmp 所持有的 heap 上的 char[] 数组来达到复用目的呢? 肯定是可以的。

3. 性能优化方案

这里需要用 右值引用 + 移动构造函数 让 s3.str 指向 tmp.str,从而避免复制构造函数,在 StringBuilder 类中加一个方法如下:


	StringBuidler(StringBuidler&& s) {
		this->str = s.str;
		this->length = s.length;

		s.str = nullptr;
	}

然后把程序跑起来,截图如下:

聊聊 C++ 右值引用 和 移动构造函数

可以看到,深复制已经没有了,这个过程会在 return 处被调用,编译器会判断如果是右值的话,自动走 移动构造函数,没有这个函数就会走 赋值构造函数。

四: 总结

总之 右值引用 可以让你尽可能的复用一些中间对象,达到一个性能上的提升,其实对 C# 程序员来说,这么简单的引用赋值,C++ 搞出了这么多概念,真的很难理解,可能还是那句话,这是 C++ 的值类型优先的基因决定的。

本文链接:https://www.dzdvip.com/35875.html 版权声明:本文内容均来源于互联网。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 395045033@qq.com,一经查实,本站将立刻删除。
(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022年7月25日 23:01
下一篇 2022年7月25日 23:15

相关推荐

  • 价格差不多的情况下选择安卓手机还是苹果手机?

    最近接到这个问答,我感觉挺有意义的,想和大家讨论一波。 咱们不往多的说,咱们就说6000块钱左右的价位吧。   在这个价位,苹果有两个选择,苹果11和苹果12,但是同价位有很多安卓手机基本都已经达到了旗舰的标准了,比如oppo Find x3 pro,小米11ultra,vivo x60pro+,一加9pro,这里不包括华为和荣耀,因为这个价位还买不到华为和荣耀的旗舰水平。 如果是我,我还是会选择安卓 首先,苹果手机有一个致命的缺点,就是快充,现在苹果手机的快充依然还在15瓦和20瓦之间逗留,但是安卓手机基本都已经60瓦起步,最高120瓦,以后还有200瓦,现在人们的生活节奏都比较快,充电慢,真的是很烦人的。只要你给支持120瓦功率的手机充上个五分钟到十分钟,基本就完全够用了,随便找个插座充一下就OK了。元气直接拉满。   再有就是现在安卓的旗舰机并没有那么不堪呀,其实我用手机是有一个习惯,我绝对不会用到三年以上,首先,对于30岁以下的人,我感觉是这样的,如果老是用一款手机的话,会没有新鲜感,体验不到时代进步的乐趣,所以我会选择更全面的安卓手机,还有就是,我感觉安卓的旗舰用个两三年是绝对没有问题。 我很好奇几点   的确苹果手机的性能很好,然后用个几年也不卡,但是长时间用一款手机,真的就像经常吃一道菜一样,没有那种味道了。 还有就是现在安卓手机的旗舰基本上都是全能的,不管是从性能,屏幕,拍照,快充就不用说了,快充肯定比苹果的好,我就很好奇,干嘛去买苹果呢?我不相信每个人买苹果都用个五年左右或者来说也是为了尝尝鲜,用个两年左右,然后卖到二手市场。 我更好奇的是,放着现在的新品手机不去买,还去买几年的二手苹果手机,我真的是有点看不懂,我记得有一个评论,之前怼过我的,他说:“你不懂的还多着呢。”

    2021年8月21日
    13
  • 你这背景太假了是什么意思(抖音你这背景太假了是什么梗)

    部分小伙伴不是很清楚你这背景太假了是什么梗?这这其实出自视频作者“疆城阿力木”,下面就由小编给大家带来你这背景太假了梗介绍,希望能给大家带来帮助。 这背景太假了是什么梗呢?最近抖音网红梗越来越多,而“你这背景太假了”就是其中之一,那么,你这背景太假了是什么意思呢? 你这背景太假了梗出自哪里呢?下面就一起来看看吧。 你这背景太假了是什么梗 此梗是出自抖音视频作者疆城阿力木”,因为本人画风粗狂,与背景不搭调,所以网友觉得背景不真实,因此出现了此梗。 你这背景太假了是什么梗 你这背景太假了梗出自哪里(图片来源:网络) 你这背景太假了梗出自哪里 这其实出自视频作者“疆城阿力木”的直播画面,但因为他狂野的外形,使得他和后面的风景格格不入,于是就有人说:你这背景太假了,于是作者直接冲入河中证明。 你这背景太假了是什么梗 你这背景太假了梗出自哪里(图片来源:网络)   你这背景太假了梗出处介绍 这其实出自视频作者“疆城阿力木”的直播画面。 但因为他狂野的外形,使得他和后面的风景格格不入 于是就有人说:你这背景太假了,于是作者直接冲入河中证明。 以上便是小编为大家带来的你这背景太假了梗出处介绍。

    2022年7月26日
    86
  • 怎么做更好的广告投放呢?

    如何做更好的广告投放呢?要想做广告投放那就先了解一下广告投放的流程。 广告投放流程: 一、开户:1找平台2招代理商 二、前期策划:1明确客户需求2制定策略 三、执行准备:搭建账号、制作投放落地页、素材搭建,广告计划搭建、提交投放计划平审核 四、正式投放:设置投放策略、开启广告、数据分析,关键节点数据分析 五、投放总结:投放数据总结、关键词策略分析 其实,互联网广告很广大,不再像肯花钱就可以见到投放效果的那个时代,投放除了有钱外,还需要能找准、找对广告渠道,才能占领渠道、占领用户。另外,在广告投放之前必须弄清楚用户的基本情况。 建议先点赞 收藏防丢失,绝对私藏干货,看到就是赚到,你绝对会回来谢我的~ 1)明确人群目标 用户画像:受众群体是谁、有什么特征 基本属性:性别、年龄、消费情况、学历水平 兴趣爱好:喜欢什么运动、喜欢什么电影、爱吃什么美食 心理属性:喜欢哪一类东西 社会属性:从事什么行业的 2)适合投放的渠道 推荐知乎平台 1内容运营——知乎 知乎平台汇聚 3500万 中产准中产用户用户分布在一二三线及东部沿海发达城市。用户无法抗拒的推送形式,抢占消费者信息接触的第一阵地,高度吸引关注。 知乎平台的广告类型有:信息流广告、视频广告、首页广告、回答页广告。 2自媒体平台——快手、小红书。 那么如何做好知乎的投放? 1做好内容输出的质量2搜索关键词3利用知+工具 那么广告投放做好需要注意的有哪些方面? 1要原创内容,内容要高质量,真实、新颖。 在互联网上要想把你的内容推广那就要跟别人不一样,而且还要干货,需要把内容搞好。 2最终的广告效果 在最后的结果关键节点上要把控好,确保内容质量效果。 今天的干货就分享到这里。如果内容对你有帮助,就不要吝啬点个赞哦,我会继续为大家提供更多专业内容!

    2021年6月24日
    19
  • 象征友谊的花(象征友谊的花是什么花)

    象征友谊的花卉有虎眼万年青、白色康乃馨、野蔷薇、刺槐、栀子花、秋海棠等,其中虎眼万年青的颜色始终为绿色,有着友谊永存的意思,而白色康乃馨可以表达纯洁的友谊,栀子花的花色为白色,代表单纯美好的友谊。” 咱们人生中,除了父母亲人,陪伴我们最多的应该就是朋友了。朋友这个角色还是非常重要的,朋友会跟我们一起分享开心,也会跟我们一起分享难过。在花卉当中,也有一些花是专门用来表示友情的,几天花花就给大家说几种象征友谊的花卉,喜欢的花友可以在家里养一盆或者送给朋友们哦~ 1、六出花六出花又叫做智利百合,这种花兼具了水仙、杜鹃、百合等花卉的特点,颜值非常高。它的花朵长得有点像是水仙花,仔细看看也非常像杜鹃花。六出花的叶子长得也非常像是百合花的叶子,所以有人也会将六出花叫做水仙百合。它寓意喜悦、友谊、健康,非常适合送给朋友养护。 如何养六出花? 六出花喜欢温暖湿润的环境,最适合六出花生长的温度是15-25摄氏度。还算是比较耐阴,但是也得有明亮的光线才可以哦~养护的时候最好是用透水透气效果比较好的土壤,可以用沙子、腐叶土按照1:1的比例配成土,用来栽种六出花。 2、文竹文竹的枝条轻轻柔柔,看起来文质彬彬的,而且开花素净典雅,非常适合象征纯洁的友谊,用来送给朋友也是寓意友情天长地久的。文竹在古代也是大户人家喜欢养护的植物,花友们喜欢文竹吗? 如何养护文竹? 文竹喜欢阴凉通风的环境,花友们在养护的时候,尽量养在光照比较少的北边窗台,这样不会灼伤文竹的叶子,否则这么热的天,只需要一个中午就能将文竹给晒成干草了。 3、勿忘我勿忘我光听这个名字就觉得非常烂漫了,也寓意着朋友之间不要互相忘记,要彼此铭记。勿忘我又叫勿忘草,是一种一年生的草花,很多花店都会用这种花当做配材来搭配花束。这种花不光能够当做绿化带的花卉欣赏,也能做成干花插在家里的花瓶里。那么勿忘我的盆栽该怎么养护呢? 如何养护勿忘我? 勿忘我还算是比较皮实的一种花,花友们可以将勿忘我用园土养护,但是养在室内的时候要土壤消毒之后再用哦~它不耐潮湿,最好是养在比较干爽的地方,还有就是保证光照充足但是不强烈,夏天不能积水,否则就容易烂根了。 4、常青藤常青藤寓意着友情之间一世常青,而且常青藤本身就代表着友谊、忠诚、永不分离等美好寓意 ,还象征着青春不朽,有活力等。 如何养护常青藤? 常青藤本身喜欢阴凉、通风、湿润的环境,花友们可以用喷壶给常青藤…

    2022年6月5日
    12
  • 辞去工作专门做自媒体靠谱吗?

    首先自媒体不是三天两天就能做起来的,做自媒体需要专业的学习和训练,自媒体刚开始流行的时候你随便搬运一些稿子,或者做伪原创都能赚到钱,现在每个平台的审核机制越来越完善,你再去做洗稿伪原创肯定行不通了。

    2021年5月25日
    8
  • 产品必懂的数据分析技巧有哪些?

    随着人口和流量红利的下降,互联网行业必然会朝着精益化运营的方向发展。数据分析在很多互联网人的工作中越发显得重要,而对于产品经理来说,更是如此。

    2021年4月28日
    18