【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.rhkb.cn/news/216506.html

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

相关文章

把 Windows 11 装进移动硬盘:Windows 11 To Go

本篇文章聊聊如何制作一个可以“说带走就带走”的 Windows 操作系统&#xff0c;将 Windows11 做成能够放在 U 盘或者移动硬盘里的 WinToGo “绿色软件”。 写在前面 在《开源的全能维护 U 盘工具&#xff1a;Ventoy》这篇文章的最后&#xff0c;我提到了一个关键词 “WinToG…

【计算机网络】HTTP响应报文Cookie原理

目录 HTTP响应报文格式 一. 状态行 状态码与状态码描述 二. 响应头 Cookie原理 一. 前因 二. Cookie的状态管理 结束语 HTTP响应报文格式 HTTP响应报文分为四部分 状态行&#xff1a;包含三部分&#xff1a;协议版本&#xff0c;状态码&#xff0c;状态码描述响应头&a…

第三届《我们的世界》---2023 国际当代艺术展在广州沙面隆重启幕

开幕快讯 2023年12月10日下午,由法国表现主义画院与东方荟萃艺术学院 联合主办的,由法中艺术交流协会、香港博物馆世界、让米歇尔艺术空 间共同协办,法国驻广州总领事馆支持的第三届《我们的世界》---2023 国际当代艺术展在广州沙面隆重启幕! 嘉宾签到现场 本次展览集合了30位活…

多平台展示预约的服装小程序效果如何

线下实体服装店非常多&#xff0c;主要以同城生意为主&#xff0c;但随着电商经济增长&#xff0c;传统线下自然流量变少&#xff0c;商家们会选择线上入驻平台开店获得更多线上用户&#xff0c;包括自建私域小程序等。 而除了直接卖货外&#xff0c;线上展示预约在服装行业也…

第16章 网络io与io多路复用select/pool/epool

第16.1节 写一个服务端代码 服务端代码 #include <stdio.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h>#include <fcntl.h>int main() {//openint sockfd sock…

C语言指针基础题(二)

目录 例题一题目解析及答案 例题二题目解析及答案 例题三题目解析及答案 例题四题目解析及答案 例题五题目解析及答案 感谢各位大佬对我的支持,如果我的文章对你有用,欢迎点击以下链接 &#x1f412;&#x1f412;&#x1f412; 个人主页 &#x1f978;&#x1f978;&#x1f…

Tomcat头上有个叉叉

问题原因&#xff1a; 这是因为它就是个空的tomcat,并没有导入项目运行 解决方案&#xff1a; war模式&#xff1a;发布模式&#xff0c;正式发布时用&#xff0c;将WEB工程以war包的形式上传到服务器 war exploded模式&#xff1a;开发时用&#xff0c;将WEB工程的文件夹直接…

虚拟现实三维电子沙盘数字沙盘开发教程第5课

虚拟现实三维电子沙盘数字沙盘无人机倾斜摄影全景建模开发教程第5课 设置system.ini 如下内容 Server122.112.229.220 userGisTest Passwordchinamtouch.com 该数据库中只提供 成都市火车南站附近的数据请注意&#xff0c;104.0648,30.61658 在鼠标指定的位置增加自己的UI对象&…

Qt 5.15.2 三维显示功能

Qt 5.15.2 三维显示功能 三维显示效果&#xff1a; .pro项目文件 QT core gui opengl 3dcore 3drender 3dinput 3dextrasgreaterThan(QT_MAJOR_VERSION, 4): QT widgetsCONFIG c17# You can make your code fail to compile if it uses deprecated APIs. # In ord…

虚幻学习笔记10—C++函数与蓝图的通信

一、前言 除了上一章C变量与蓝图通信讲的变量能与蓝图通信外&#xff0c;还有函数和枚举也可以和蓝图通信。函数的关键字为”UFUNCTION“、枚举的关键字为”UENUM“。 二、实现 2.1、BlueprintCallable蓝图中调用 该函数时带执行的&#xff0c;带入如下。编译成功后在蓝图中输…

Android 11 适配——整理总结篇

背景 > 经过检测&#xff0c;我们识别到您的应用&#xff0c;目前未适配安卓11&#xff08;API30&#xff09;&#xff0c;请您关注适配截止时间&#xff0c;尽快开展适配工作&#xff0c;避免影响应用正常发布和经营。 > targetSdkVersion30 升级适配工作参考文档&am…

【算法】递归、搜索与回溯算法

文章目录 一. 名词解释1. 递归1.1 什么是递归&#xff1f;1.2 为什么会用到递归&#xff1f;1.3 如何理解递归&#xff1f;1.4 如何写好一个递归&#xff1f; 2. 遍历和搜索3. 回溯和剪枝 二. 递归系列专题1. 汉诺塔问题2. 合并两个有序链表3. 反转链表4. 两两交换链表中的节点…

git自动更新功能

确认权限 因为一般Linux系统网页用的www 或 www-data用户和用户组&#xff0c;所以要实现自动来去&#xff0c;首先要在www用户权限下生成ssh密钥&#xff0c;不然没有权限&#xff0c;其次就是&#xff0c;要把用root用户拉去的代码&#xff0c;批量改成www用户 1. 给www权…

Unity | 渡鸦避难所-2 | 搭建场景并添加碰撞器

1 规范项目结构 上期中在导入一系列的商店资源包后&#xff0c;Assets 目录已经变的混乱不堪 开发过程中&#xff0c;随着资源不断更新&#xff0c;遵循一定的项目结构和设计规范是非常必要的。这可以增加项目的可读性、维护性、扩展性以及提高团队协作效率 这里先做下简单的…

最新鸿蒙HarmonyOS4.0开发登陆的界面1

下载deveco-studio 说明一下&#xff0c;本人只是学习中&#xff0c;现在只是拿着vue及uniapp的经验在一点一点的折腾&#xff0c;不过现在看来&#xff0c;鸿蒙入门并不是很难。也许是自己没有深入下去。 https://developer.harmonyos.com/cn/develop/deveco-studio#download…

Ubuntu 20.04 安装 mysql8 LTS

Ubuntu 20.04 安装 mysql8 LTS sudo apt-get update sudo apt-get install mysql-server -y mysql --version mysql Ver 8.0.35-0ubuntu0.20.04.1 for Linux on x86_64 ((Ubuntu)) Ubuntu20.04 是自带了 MySQL8. 几版本的&#xff0c;低于 20.04 则默认安装是 MySQL5.7.33…

基于Java8构建Docke镜像

基于Java8构建Docke镜像 搜索java8安装包 docker search java8 --no-trunc &#xff0c; --no-trunc展开描述信息 选择拉取 docker pull docker.io/mykro/java8-jre&#xff0c;为了减少磁盘占用&#xff0c;选择jre版本基础镜像 在宿主机创建文件夹iot&#xff0c;并把所需…

IDEA中,光标移动快捷键(Shift + 滚轮前后滚动:当前文件的横向滚动轴滚动。)

除此之外&#xff0c;其他常用的光标移动快捷键包括&#xff1a; Shift 滚轮前后滚动&#xff1a;当前文件的横向滚动轴滚动。Shiftenter&#xff1a;快速将鼠标移动到下一行。Ctrl ]&#xff1a;移动光标到当前所在代码的花括号结束位置。Ctrl 左方向键&#xff1a;光标跳转…

VisualSVN Server的安装全过程

目录 背景: 安装过程&#xff1a; 步骤1&#xff1a; 步骤2&#xff1a; 步骤3&#xff1a; 步骤4&#xff1a; 步骤5&#xff1a; 安装出现的bug&#xff1a; 问题: 解决办法: 总结: 背景: VisualSVN Server 是一款免费的 SVN (Subversion) 服务器软件&#xff0c…

大数据技术8:StarRocks极速全场景MPP数据库

前言&#xff1a;StarRocks原名DorisDB&#xff0c;是新一代极速全场景MPP数据库。StarRocks 是 Apache Doris 的 Fork 版本。StarRocks 连接的多种源。一是通过这个 CDC 或者说通过这个 ETL 的方式去灌到这个 StarRocks 里面&#xff1b;二是还可以去直接的和这些老的 kafka 或…