移动构造-C++11

2023-11-10

移动构造:

移动构造是C++11标准中提供的一种新的构造方法。

在现实中有很多这样的例子,我们将钱从一个账号转移到另一个账号,将手机SIM卡转移到另一台手机,将文件从一个位置剪切到另一个位置……

    移动构造可以减少不必要的复制,带来性能上的提升。

有些复制构造是必要的,我们确实需要另外一个副本;而有些复制构造是不必要的,我们可能只是希望这个对象换个地方,移动一下而已。

在C++11之前,如果要将源对象的状态转移到目标对象只能通过复制。

而现在在某些情况下,我们没有必要复制对象——只需要移动它们


C++11引入移动语义:

    ~源对象资源的控制权全部交给目标对象

对比一下复制构造和移动构造:

复制构造是这样的:

在对象被复制后临时对象和复制构造的对象各自占有不同的同样大小的堆内存,就是一个副本。

                                                     

移动构造是这样的:

就是让这个临时对象它原本控制的内存的空间转移给构造出来的对象,这样就相当于把它移动过去了。

                                                       


复制构造和移动构造的差别:

    这种情况下,我们觉得这个临时对象完成了复制构造后,就不需要它了,我们就没有必要去首先产生一个副本,然后析构这个临时对象,这样费两遍事,又占用内存空间,所幸将临时对象它的原本的资源直接转给构造的对象即可了。

    当临时对象在被复制后,就不再被利用了。我们完全可以把临时对象的资源直接移动,这样就避免了多余的复制构造。

什么时候该触发移动构造呢?

    如果临时对象即将消亡,并且它里面的资源是需要被再利用的,这个时候我们就可以触发移动构造。


移动构造是需要通过移动构造函数来完成的。

移动构造函数定义形式:

class_name(class_name && )

例:函数返回含有指针成员的对象

有两种版本:

版本一:使用深层复制构造函数

    ~ 返回时构造临时对象,动态分配临时对象返回到主调函数,然后删除临时对象。

版本二:使用移动构造函数

    ~ 将要返回的局部对象转移到主调函数,省去了构造和删除临时对象的过过程。


版本一举例:使用复制构造的代码:

[cpp]  view plain  copy
  1. #include<iostream>    
  2. using namespace std;    
  3. class IntNum{    
  4. public:    
  5.      IntNum(int x = 0):xptr(new int(x)){//构造函数    
  6.        cout<<"Calling constructor..."<<endl;    
  7. }    
  8.     IntNum(const IntNum &n):xptr(new int(*n.xpr)){//复制构造函数    
  9.          cout<<"Calling copy constructor..."<<endl;    
  10. }    
  11.        ~IntNum(){//析构函数    
  12.         delete xpr;    
  13.         cout<<"Destructing..."<<endl;        
  14. }    
  15.         int getInt(){return *ptr;}//返回指针所指向的值,而不是返回指针本身    
  16. private:    
  17.          int *ptr;    
  18. };    
  19.     
  20. //返回值为IntNum类对象    
  21. IntNum getNum(){    
  22.      //定义了一个局部对象,然后将局部对象作为结果返回    
  23.       IntNum a;    
  24.     //返回值是IntNum类型    
  25.       return a;    
  26. }    
  27.  int main(){    
  28.    //getNum()函数返回了一个IntNum类型的对象(临时无名对象),之后调用类的函数    
  29.     cout<<"getNum().getInt()"<<endl;    
  30.     return 0;    
  31. }    


版本一通过深层复制构造函数实现的返回含有指针成员的对象,在这个过程中,做了一些无用功,既需要构造临时无名(注:是无名对象,不是上例中的对象a)对象,还要对其进行析构。

在上例中不构造临时无名对象不行吗?其实呢我们也不用构造临时无名对象了,对象a反正是要消亡的,我们把要消亡的对象a占用的资源转移给临时对象,不行吗?!这样的方案就是使用移动构造

版本二:使用移动构造

使用移动构造函数的代码举例:

[cpp]  view plain  copy
  1. #include<iostream>    
  2. using namespace std;    
  3. class IntNum{    
  4. public:    
  5.      IntNum(int x = 0):xptr(new int(x)){//构造函数    
  6.        cout<<"Calling constructor..."<<endl;    
  7. }    
  8.     IntNum(const IntNum &n):xptr(new int(*n.xpr)){//复制构造函数    
  9.          cout<<"Calling copy constructor..."<<endl;    
  10. }   
  11.    IntNum(IntNum && n):xptr(n.xptr){//移动构造函数  
  12.      n.xptr = nullptr;  
  13.      cout<<"Calling move constructor..."<<endl;  
  14. }  
  15.        ~IntNum(){//析构函数    
  16.         delete xpr;    
  17.         cout<<"Destructing..."<<endl;  
  18. }   
  19.         int getInt(){return *ptr;}//返回指针所指向的值,而不是返回指针本身    
  20. private:    
  21.          int *ptr;    
  22. };    
  23.     
  24. //返回值为IntNum类对象    
  25. IntNum getNum(){    
  26.      //定义了一个局部对象,然后将局部对象作为结果返回    
  27.       IntNum a;    
  28.     //返回值是IntNum类型    
  29.       return a;    
  30. }    
  31.   int main(){    
  32.    //getNum()函数返回了一个IntNum类型的对象(临时无名对象),之后调用类的函数    
  33.     cout<<"getNum().getInt()"<<endl;    
  34.     return 0;    
  35. }    

在该例中的移动构造函数

IntNum(IntNum && n):xptr(n.xptr){//移动构造函数
     n.xptr = nullptr;
     cout<<"Calling move constructor..."<<endl;
}

好像干了件很危险的事情:直接用参数对象(n)里面的指针(n.xptr)来初始化当前对象的指针(xptr),(按理说这不是浅层复制吗?!说了有指针成员,我们复制时不能做这种浅层复制的呀,怎么在这里我们恰恰做浅层复制了呢)

看函数体里面,我们发现再做完xptr(n.xptr)这种指针对指针的复制(也就是把参数指针所指向的对象转给了当前正在被构造的指针)后,接着就把参数n里面的指针置为空指针(n.xptr = nullptr;),对象里面的指针置为空指针后,将来析构函数析构该指针(delete xpr;)时,是delete一个空指针,不发生任何事情,这就是一个移动构造函数。

移动构造函数中的参数类型,&&符号表示是右值引用;即将消亡的值就是右值,函数返回的临时变量也是右值,这样的单个的这样的引用可以绑定到左值的,而这个引用它可以绑定到即将消亡的对象,绑定到右值


左值和右值都是针对表达式而言的,左值是指表达式结束后依然存在的持久对象,右值指表达式结束时就不再存在的临时对象——显然右值不可以被取地址。


关于左值、右值和右值引用的详细内容可以参考:http://www.ibm.com/developerworks/cn/aix/library/1307_lisl_c11/



FROM:  http://blog.csdn.net/zyq11223/article/details/48766515

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

移动构造-C++11 的相关文章

  • 手写android Log 源码(简单版)

    有一天晚上 看了两篇老罗的博客 Android日志系统Logcat源代码简要分析 和 Android日志系统驱动程序Logger源代码分析 于是我就想android10 中 log 是怎么实现的 然后又看了一篇别人的博客 Android10
  • C++ STL : std::list

    练习下C STL中std list类的常用方法 方便以后查阅 如有不正确的地方 请读者及时指正 欢迎转载 谢谢 include
  • 构造函数属性为protected或者private时

    在c 中 不仅限于c 一个函数被声明为protected或者private时 那也就意味着不能被外部直接调用了 类的成员函数add 是private class cla private int add int a int b return
  • 字符检测:C语言ispunct()函数--判断字符是否为标点符号或特殊字符

    ispunct 函数用来检测一个字符是否为标点符号或特殊字符 其原型为 int ispunct int c 参数 c 为需要检测的字符 返回值 若 c 为标点符号或特殊符号 非空格 非数字和非英文字母 返回非 0 值 否则返回 0 注意 此
  • 显式调用构造函数和析构函数

    今天跟同事聊天 他说到STL源码有用到显示调用析构函数 试一了一下 果然能行 include lt iostream gt using namespace std class MyClass public MyClass cout lt l
  • windows系统c++多线程开发

    线程的一些基本概念 一 线程的基本概念 基本概念 线程 即轻量级进程 LWP LightWeight Process 是程序执行流的最小单元 一个标准的线程由线程ID 当前指令指针 PC 寄存器集合和堆栈组成 线程是进程中的一个实体 是被系
  • 两个C++编译异常及解决方法-does not name a type和field `XX' has incomplete type

    两个C 编译错误及解决办法 does not name a type和field XX has incomplete type 编译错误一 XX does not name a type 编译错误二 field XX has incompl
  • C++异常处理机制详解

    异常处理是一种允许两个独立开发的程序组件在程序执行期间遇到程序不正常的情况 异常exception 时相互通信的机制 本文总结了19个C 异常处理中的常见问题 基本涵盖了一般C 程序开发所需的关于异常处理部分的细节 1 throw可以抛出哪
  • Qt C++中的关键字explicit

    最近在复习QT 准备做项目了 QT Creator 默认生成的代码 explicit Dialog QWidget parent 0 中 有这么一个关键字explicit 用来修饰构造函数 以前在Windows下写程序的时候 基本上没有碰到
  • C++ 创建共享内存

    共享内存用于实现进程间大量的数据传输 共享内存是在内存中单独开辟一段内存空间 这段内存空间有自己特有的数据结构 包括访问权限 大小和最近访问时间等 1 shmget函数 include
  • C++ 构造函数和析构函数是否可以继承?

    先看一个例子 cpp view plain copy include
  • 马虎的算式

    标题 马虎的算式 小明是个急性子 上小学的时候经常把老师写在黑板上的题目抄错了 有一次 老师出的题目是 36 x 495 他却给抄成了 396 x 45 但结果却很戏剧性 他的答案竟然是对的 因为 36 495 396 45 17820 类
  • 字符编码与C++

    背景 C 的项目 字符编码是一个大坑 不同平台之间的编码往往不一样 如果不同编码格式用一套字符读取格式读取就会出现乱码 所以本文旨在对字符编码的知识做一个大概的梳理 字符编码定义 计算机是以二进制的形式来存储数据的 它只认识 0 和 1 两
  • 从Qt谈到C++(一):关键字explicit与构造函数

    原文 http blog csdn net guodongxiaren article details 24455653 主题 Qt 提出疑问 当我们新建了一个Qt的widgets应用工程时 会自动生成一个框架 包含了几个文件 其中有个ma
  • C语言中的快速排序库函数

    前言 由于自己的记性一直不好 总是记不住c语言中的快速排序函数 于是自己写下博客来记录一下 快速排序库函数 c语言中的快速排序库函数如下 注意 库函数是在stdlib h头文件中 qsort arr length size t cmp 其中
  • c语言中的字符数组和字符串之间的关系

    一 字符串的结束标志 1 很多时候我们都是可以看到相关的内容就是 使用数组来存储字符串 也就是我们经常会使用到sizeof 和这个函数 而 这个函数只是求出当前该数组的最大容量 而不是数组中实际存放的内容 我们一般都是需要使用 0 来表示字
  • c/c++资源汇总

    Visual C 视频技术方案宝典 pdf http www t00y com file 17628500 Windows 图形编程 pdf http www t00y com file 17628502 Windows程序设计 第2版 p
  • C++系列目录

    基础语言篇 C 数据类型 C位操作 C预编译处理 C指针 C结构体与枚举类型 C 函数 C 虚函数 C 容器与算法 C 类 C I O处理 C 重载操作符与转换 模板与泛型 C C 编译和调试 C C 动态链接 C C 通用MakeFile
  • 如何零基础自学c/c++语言?

    现在零基础学习C C 无非就两种方法 一种是自学还有 一种就是报班学习 关于报班学习在这里就不多说了 那么今天就说怎么从零基础开始自学C C 编程吧 先学习C语言入门 那么问题来了 怎么去学习C语言呢 一开始肯定是要看书 这里推荐的入门书籍
  • 类的数组成员变量的初始化

    使用STL标准模板库之后 编程时已经很少使用数组和指针 相反 多使用序列容器vector代替之 但事实并不这么理想 在迫不得已的情况下 我们还是会选择使用数组 这里介绍一下当数组作为类的成员变量时 应该怎么对它 数组 进行初始化 在类的构造

随机推荐

  • 20_删除某个文件夹RemoveDirectory()

    删除某个文件夹RemoveDirectory 思想是RemoveDirectory 只能删除一个空的目录 如果目录中有子目录或者是子文件的话就会删除失败的 解决的方法是递归的思想 将子文件删除 之后就可以调用这个函数删除一个空的文件夹了 删
  • 华为OD机试真题-座位调整-2023年OD统一考试(B卷)

    题目描述 疫情期间课堂的座位进行了特殊的调整 不能出现两个同学紧挨着 必须隔至少一个空位 给你一个整数数组 desk表示当前座位的占座情况 由若干 0 和 1 组成 其中 0 表示没有占位 1 表示占位 在不改变原有座位秩序情况下 还能安排
  • 使用ffmpeg+nginx+flvjs实现web播放rtsp视频流

    文章目录 1 简介 2 安装ffmpeg 3 安装nginx 3 1 安装nginx依赖 3 1 1 pcre 3 1 2 zlib 3 1 3 openssl 3 2 编译nginx 3 2 1 下载nginx 3 2 2 下载nginx
  • AWS、Azure、谷歌云、阿里云最新全方位比较

    Gartner预测 到2022年底 全球云计算支出将达到4820亿美元 比2021增长21 7 随着云计算成为大多数企业的优先事项 了解市场上顶级基础设施即服务 IaaS 提供商之间的差异也变得越来越重要 在云迁移方面 成本 可扩展性和数据
  • 美国50州分布图、重要城市分布图、大学分布图、NBA球队分布图、著名景点分布图

    文章目录 1 美国50州分布图 2 美国重要城市分布图 3 美国大学分布图 4 NBA球队分布图 5 美国著名景点分布图 6 奇奇怪怪的有趣知识 1 美国50州分布图 2 美国重要城市分布图 深入了解传送门 https www meet99
  • U-boot的环境变量: bootcmd 和bootargs

    bootcmd bootcmd是自动启动时默认执行的一些命令 因此你可以在当前环境中定义各种不同配置 不同环境的参数设置 然后设置bootcmd为你经常使用的那种参数 bootargs bootargs是环境变量中的重中之重 甚至可以说整个
  • 将本地项目上传到新的git仓库的流程

    将本地项目上传到新建一个git仓库的流程 1 在项目托管平台github上新建一个仓库 复制仓库地址 2 在本地项目目录下输入 git init git add all git commit m lt 描述 gt 3 在本地项目目录下输入
  • mos管开关判断

    http wenku baidu com view 0983941cc281e53a5802ffd1 html MOS管及MOS管的驱动电路设计 MOS管及MOS管的驱动电路设计 摘要 本文将对MOSFET的种类 结构 特性及应用电路作一简
  • 华为交换机查看端口流量_华为交换机端口镜像配置

    端口镜像 通过配置镜像功能 可以将报文复制到特定的目的地进行分析 以进行网络监控和故障定位 镜像是指将经过指定端口 源端口或者镜像端口 的报文复制一份到另一个指定端口 目的端口或者观察端口 配置举例 1 一对一本地端口镜像 一个监控设备监控
  • 请读下面的这句绕口令:ResourceManager中的Resource Estimator框架介绍与算法剖析

    欢迎大家前往腾讯云 社区 获取更多腾讯海量技术实践干货哦 本文由宋超发表于云 社区专栏 本文首先介绍了Hadoop中的ResourceManager中的estimator service的框架与运行流程 然后对其中用到的资源估算算法进行了原
  • spring的IOC容器

    文章目录 1 IOC IOC容器 Bean DI 2 核心概念小结 3 入门案例 3 1 IOC入门案例 3 1 1 入门案例思路分析 3 1 2 入门案例代码实现 步骤1 创建Maven项目 步骤2 添加Spring的依赖jar包 步骤3
  • Python中常见的错误之一是[ImportError: attempted relative import with no known parent pack...

    Python中常见的错误之一是 ImportError attempted relative import with no known parent package 该错误通常在导入相对路径时出现 本文将深入分析该错误的原因 并提供几种解决
  • STM32通过串口控制42步进电机

    上面是我的微信和QQ群 欢迎新朋友的加入 最近在做42步进电机的东西 记录一下问题 目录 1 脉冲输出 2 硬件设计 3 嵌入式软件设计 4 控制软件设计 5 测试使用 1 脉冲输出 主控芯片是STM32F030 主要就是便宜 脉冲输出最开
  • 爬虫入门第4课:定义代理IP的数据模型类

    爬虫学习知识点及案例篇 汇总 爬虫入门第1课 代理池概述及开发环境 爬虫入门第2课 代理池的设计 爬虫入门第3课 实现代理池思路 本阶段带大家从代理池的设计开始 学习Python爬虫及项目实战 详情关注上方专栏 目标 定义代理IP的数据模型
  • 粗略的计算PCD点云的体积和表面积(非精确计算,python代码)

    读取的点云数据进行凸包计算 使用PointCloud对象的compute convex hull方法 返回凸包模型和凸包模型中点的索引 给凸包模型渲染颜色 使用TriangleMesh对象的paint uniform color方法 计算凸
  • 创建jira plugin插件、创建jira实例生成jar包总结

    创建jira plugin插件 创建jira实例生成jar包总结 创建插件骨架 在本地安装完Atlassian SDK后 需注意将环境变量里的maven路径更改为sdk里自带的maven仓库路径 进入Atlassian的bin目录下 在此路
  • unity导入的模型无法编辑材质球属性

    有时候美术做好的模型导入到unity 发现无法编辑材质的shader 如图 选中对应的模型 在inspector面板上更改一个设置 一般就可以了 将location从use embedded materials 改成use external
  • Java原始客户端操作Mongodb 增删改查

    Document方式操作增删改查 1 导入Pom依赖 2 java客户端代码 1 导入Pom依赖
  • 全面理解网络流中的最大流问题

    网络流 最大流问题 前序 在将网络里实现算法之前 我们得聊聊网络流究竟是个什么东西 毕竟只有知道它的样貌 才能继续看懂下面的定义 对吧 首先 网络流不仅仅指的是什么FF算法 dinic算法 算法只是用来解决问题的 稍后我们会更加能体会这一点
  • 移动构造-C++11

    移动构造 移动构造是C 11标准中提供的一种新的构造方法 在现实中有很多这样的例子 我们将钱从一个账号转移到另一个账号 将手机SIM卡转移到另一台手机 将文件从一个位置剪切到另一个位置 移动构造可以减少不必要的复制 带来性能上的提升 有些复