【C++11】右值引用与移动语义

一.左值与右值

左值:可以取地址的表示数据的表达式,左值可以出现在赋值符号左边

右值:不能取地址的表示数据的表达式,右值不能出现在赋值符号左边

int fun()
{return 0;
}
int main()
{int a = 0;//a->左值const int b = 1;//b->左值int* p = &a;//*p->左值a + b;//右值func();//右值10;//右值
}

二.左值引用与右值引用 

左值引用:给左值取的别名,符号:type&

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

右值引用:给右值取的别名,,符号:type&&0

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

无论左值引用还是右值引用,都是在取别名,理论上来说不会开辟额外的空间

int a = 0, b = 0;
int& ref1 = a;//左值引用给左值取别名
const int& ref2 = a + b;//临时对象具有常性,左值引用想要绑定右值要将上const
int&& ref3 = a + b;//右值引用给右值取别名
int&& ref4 = move(a);//右值引用给move以后的左值取别名

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

三.使用引用减少拷贝

当函数传参或者传返回值时,传的是自定义类型对象,如果处理不当,将会发生多次拷贝,尤其是需要深拷贝的类,大大降低效率,使用引用就可以有效解决这些问题。

1.左值引用减少拷贝

  1. 引用传参
  2. 传引用返回

左值引用的短板:当函数返回对象是局部对象,出作用域就会被销毁,此时只能传值返回,倘若该对象需要深拷贝,付出的代价是很大的。

为了解决这一问题,右值引用在C++11应运而生。

2.右值引用减少拷贝

(1)深拷贝的类DeepCopy

class DeepCopy
{
public:DeepCopy(int* p):_p(p){}~DeepCopy(){delete[] _p;}
private:int* _p;
};int main()
{DeepCopy d1 (new int[5]{ 1, 2, 3, 4, 5 });DeepCopy d2 = d1;return 0;
}

DeepCopy类就是一个需要深拷贝的类,因为它的成员着管理一块资源,析构时需要释放资源。如果使用编译器提供的浅拷贝构造,会导致同一块空间释放两次,因此需要我们自己提供深拷贝构造,同样,赋值也需要自己提供。

    DeepCopy(const DeepCopy& d){_p = new int[d._n];_n = d._n;for (int i = 0; i < _n; i++){_p[i] = d._p[i];}cout << "拷贝构造" << endl;}DeepCopy& operator=(const DeepCopy& d){if (this != &d){delete[] _p;_n = d._n;_p = new int[_n];for (int i = 0; i < _n; i++){_p[i] = d._p[i];}}cout << "拷贝赋值" << endl;return *this;}

(2)传值返回发生拷贝构造

传值返回的场景:

DeepCopy Fun()
{DeepCopy d(new int[3]{ 1,2,3 }, 3);return d;
}
int main()
{DeepCopy d1 = Fun();return 0;
}

 

整个过程如下:

由于是传值返回,所以先用d1拷贝构造一个临时对象,再用临时对象拷贝构造d。本来是分两步的,但连续的构造被编译器优化成一步。

请注意:临时对象是右值,const左值引用可以接收右值,故可以匹配拷贝构造函数

(3)传值返回发生移动构造 

我们可以将右值分为两类:

  1. 纯右值:内置类型的右值,包括字面常量,表达式结果,函数返回值等等
  2. 将亡值:自定义类型的右值,即函数返回值,如它的名字一样,它不会再被使用,马上就会被销毁。

 对于将亡值的拷贝,我们不用照着它的模版重新生成一份,而是直接转移它的资源,这样代价会小很多。因此,我们可以针对这种将亡值专门设计一个构造函数来转移资源,这种构造函数叫移动构造

    //DeepCopy中增加:DeepCopy(DeepCopy&& d){_p = nullptr;std::swap(_p, d._p);std::swap(_n, d._n);cout << "移动构造" << endl;}int main()
{DeepCopy d1(new int[5]{ 1,2,3,4,5 }, 5);d1 = Fun();return 0;
}

编译器会将返回的d1对象特殊处理,将它识别为右值,所以这里用一个右值构造临时对象。右值既虽然可以被const左值引用接收,但右值引用是更合适的,所以会匹配移动构造。同理,临时对象也是个将亡值,所以匹配的是移动构造。两次连续的构造会被编译器优化成一次移动构造。

不仅有移动构造,还有移动赋值:

    //DeepCopy中增加:	DeepCopy& operator=(DeepCopy&& d){if (this != &d){std::swap(_p, d._p);std::swap(_n, d._n);}cout << "移动赋值" << endl;return *this;}
//临时对象的资源不仅被转移走了,还得到了*this不要的资源,析构时会释放掉int main()
{DeepCopy d1(new int[5]{ 1,2,3,4,5 }, 5);d1 = Fun();return 0;
}

3.总结

引用的意义就在于减少拷贝

左值引用:直接减少拷贝:
1.引用传参 2.传引用返回

但有些场景不能传引用返回(函数内的局部对象),因此还是无法避免深拷贝
右值引用:间接减少拷贝
和const左值引用进行区分,传将亡对象拷贝时匹配移动构造函数,直接转移资源

四.完美转发

(1)属性退化

一个右值引用与右值绑定之后,这个引用的属性是左值。换句话说,右值被右值引用之后属性退化成了左值,从原来的不可修改变为可修改。如果你不想它被修改,可以用const修饰引用,但它仍然是一个左值。

从底层角度来看,实际是右值引用使数据的存储位置发生改变,可以取到数据的地址。

这也能够解释为什么移动构造或移动赋值函数中,能够转移将亡对象资源的原因,因为它的属性退化成了左值,可以被修改。

(2)万能引用

模版参数+&&=万能引用:无论是左值还是右值,无论是const还是非const,都能接收

我们可以用万能引用验证第(1)点的结论 

void fun(int& x)
{cout << "void fun(int& x)左值" << endl;
}void fun(const int& x)
{cout << "void fun(const int& x)const左值" << endl;
}void fun(int&& x)
{cout << "void fun(int&& x)右值" << endl;
}
void fun(const int&& x)
{cout << "void fun(const int&& x)const右值" << endl;
}void PerfectForward(T&& t)//T&& 万能引用
{fun(t);
}int main()
{int a = 10;PerfectForward(a);//左值PerfectForward(10);//右值const int b = 8;PerfectForward(b);//const左值PerfectForward(move(b));//const右值return 0;
}

 

(3)完美转发

如果想要在传递过程中保持对象的左值或右值属性不变,可以使用完美转发std::forward<T>(T是对象的类型)

 将上面的fun函数稍作修改:

void PerfectForward(T&& t)//T&& 万能引用
{fun(forward<T>(t));
}

 五.C++11新增的默认成员函数

C++11新增了两个默认成员函数:移动构造和移动赋值。

如果没有实现移动构造,且拷贝构造,拷贝赋值重载和析构函数都没有实现,那么编译器会生成移动构造函数,对于内置类型逐字节拷贝;对于自定义类型,如果有移动构造则去调用移动构造,没有就退而且其次,调用拷贝构造。

移动赋值的生成规则类似。

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

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

相关文章

【lesson14】MySQL表的基本查询(1)

文章目录 表的基本操作介绍retrieveselect列建表基本测试 where子句建表基本测试 表的基本操作介绍 CRUD : Create(创建), Retrieve(读取)&#xff0c;Update(更新)&#xff0c;Delete&#xff08;删除&#xff09; retrieve select列 建表 基本测试 插入数据 全列查询 …

2023年度影响力出海品牌传音移动互联:开放合作 赋能更多中国企业高效出海

伴随着全球化的脚步&#xff0c;出海成为许多中国企业的“必选项”&#xff0c;与之配套的出海服务相关业务也得到了极大的发展。近日&#xff0c;第五届鲸鸣奖颁奖典礼上&#xff0c;传音移动互联凭借为企业提供高效优质的出海解决方案&#xff0c;荣获鲸鸣奖“2023年度影响力…

SpringBoot 引入nacos 【最新 | 可运行】

SpringBoot 引入nacos 首先要了解在 Springboot 中只支持那些 Springboot 的版本&#xff08;我真的被这个搞死了&#xff09;,可以如下图参考&#xff1a; 下面我们就开始吧 下载 Nacos nacos 下载地址&#xff0c;这里可以选择你要下载的版本&#xff0c;我选择下载了2.2.…

[c]零钱兑换

题目比较简单&#xff0c;看答案就能看懂什么意思 #include<stdio.h> int main() {int count 0;int n;scanf("%d", &n);for (int i 0; i < n; i){for (int k 0; k <n/2; k){for (int j 0; j < n/5 ; j){if (i 2 * k 5 * j n){count;}}}}p…

这套软件测试技巧|软测经典面试题真的有用,今天面试大部分都遇到了!!!

祝同学们都能够顺利找到心仪的工作拿高薪&#xff0c;废话不多说&#xff0c;下面上题了~ 46、您以往是否曾经从事过性能测试工作&#xff1f;如果有&#xff0c;请尽可能的详细描述您以往的性能测试工作的完整过程。 &#xff08;以自己最熟悉的性能测试项目为例&#xff09; …

可惜+悲伤+唉=emmo...

拟合曲线&#xff1a; 参考论文&#xff1a;黄河清.NURBS曲面逆向造型关键算法的研究与应用 [D].西北工业大学,2004 三次NURBS曲线控制点的计算 首先给出拟合曲线的具体步骤&#xff1a; 1、节点矢量的求解方法为&#xff1a; 采用积累弦长参数化法&#xff0c;即&#xff1…

应用ICP-MS实验PFA烧杯耐腐蚀带刻度反应杯的特点分析

聚四氟&#xff08;PFA&#xff09;烧杯可用于痕量分析、同位素分析等实验&#xff0c;ICP-MS实验室适用。半导体、多晶硅、光伏电子 锂电池行业均适用。杯体刻度清晰&#xff0c;方便观察&#xff0c;尖嘴方便倾倒溶液。 可溶性聚四氟乙烯烧杯特性&#xff1a; 1、透明&…

(企业 / 公司项目)微服务项目解决跨域问题:

前后端分离项目中前端出现了跨域的问题 在网关模块配置文件中添加 配置 application.properties # 允许请求来源&#xff08;老版本叫allowedOrigin&#xff09; spring.cloud.gateway.globalcors.cors-configurations.[/**].allowedOriginPatterns* # 允许携带的头信息 spri…

经典基本电路

USB电路 USB差分走线的阻抗为90欧:差分对10mil宽的走线以及5mil的间距,两边包地15/20mil以上厚度(SI9000计算阻抗) USB2.0接口电路&#xff1a; USB3.0接口电路&#xff1a; USB HUB电路: HDMI电路 HDMI差分走线的阻抗为100欧:差分对6mil宽的走线以及5mil的间距,两边包地15/20…

PADS的使用

目录 一、PADS常规操作1.1 常用快捷键1.2 Logic相关设置 二、Logic库操作2.1 逻辑库2.2 封装库2.3 元件库 一、PADS常规操作 PADS安装好了之后&#xff0c;主要使用下面三个软件&#xff1a; Logic&#xff1a;画原理图文件&#xff0c;也可以使用OrCAD导入Router&#xff1a;…

解决maven报错 ‘parent.relativePath‘ of POM

错误提示 parent.relativePath of POM io.renren:renren-fast:3.0.0 (D:\wzyProjets\gulimail\renren-fast\pom.xml) points at com.wzy.gulimail:gulimail instead of org.springframework.boot:spring-boot-starter-parent, please verify your project structure错误分析 子…

windows10下jdk安装

文章目录 windows10下jdk安装说明what安装包下载执行安装包验证是否安装成功 windows10下jdk安装 说明 操作系统&#xff1a;windows10 版本&#xff1a;1.8 what JDK(Java Development Kit) 是 Java 语言的软件开发工具包 安装包下载 https://www.oracle.com/java/techn…

【JAVA】黑马MybatisPlus 学习笔记【二】【核心功能】

2.核心功能 刚才的案例中都是以id为条件的简单CRUD&#xff0c;一些复杂条件的SQL语句就要用到一些更高级的功能了。 2.1.条件构造器 除了新增以外&#xff0c;修改、删除、查询的SQL语句都需要指定where条件。因此BaseMapper中提供的相关方法除了以id作为where条件以外&…

Proxmox创建Windows虚拟机

文章目录 下载ISO安装文件上传 下载ISO安装文件 下载地址&#xff1a;https://www.xitongzhijia.net/ 也可去官网进行下载 上传 将下载的ISO文件上传到Proxmox 选择ISO文件进行上传 上传后再ISO镜像中可以看到安装文件 点击创建虚拟机 填写名称&#xff0c;不能填写中文 镜…

被围绕的区域[中等]

一、题目 给你一个m x n的矩阵board&#xff0c;由若干字符X和O&#xff0c;找到所有被X围绕的区域&#xff0c;并将这些区域里所有的O用X填充。 示例 1&#xff1a; 输入&#xff1a;board [["X","X","X","X"],["X",&qu…

aardio网页组件:webPageOperation

webPageOperation是webview的初步封装&#xff0c;用来网页填表、操作网页。可操作web.form、web.view、web.view2等浏览器组件。 使用方法 首先把webPageOperation.aardio&#xff08;源码在后面&#xff09;放到~\lib\godking目录下&#xff0c;然后新建窗口项目&#xff…

HarmonyOS开发入门HelloWorld及工具安装

下载与安装DevEco Studio 在HarmonyOS应用开发学习之前&#xff0c;需要进行一些准备工作&#xff0c;首先需要完成开发工具DevEco Studio的下载与安装以及环境配置。 进入DevEco Studio下载官网&#xff0c;单击“立即下载”进入下载页面。 DevEco Studio提供了Windows版本和…

mysql:在字符串类型的列上创建索引,建议指定索引前缀长度

https://dev.mysql.com/doc/refman/8.2/en/create-index.html#create-index-column-prefixes 在字符串类型的列上创建索引&#xff0c;建议指定索引前缀长度&#xff0c;而没有必要用整个列来创建索引。因为用前面的字符创建索引&#xff0c;查询时并不会比在整列上创建索引慢很…

【LeetCode刷题】-- 165.比较版本号

165.比较版本号 方法&#xff1a;使用双指针 class Solution {public int compareVersion(String version1, String version2) {//使用双指针int n version1.length(),m version2.length();int i 0,j 0;while(i<n || j <m){int x 0;for(; i < n && vers…

鸿蒙OS应用开发者高级认证题库

一、判断题 云函数打包完成后&#xff0c;需要到AppGallery Connect创建对应函数的触发器才可以在端侧中调用&#xff08;错&#xff09; 在column和Row容器组件中&#xff0c;aligntems用于设置子组件在主轴方向上的对齐格式&#xff0c;justifycontent用于设置子组件在交叉轴…