【C++ | 移动构造函数】C++11的 移动构造函数 详解及例子代码

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
⏰发布时间⏰:2024-06-12 23:36:22

本文未经允许,不得转发!!!

目录

  • 🎄一、概述
    • ✨1.1 移动语义
    • ✨1.2 右值引用
  • 🎄二、移动构造函数(move constructor)
  • 🎄三、使用移动构造函数
  • 🎄四、总结



在这里插入图片描述

🎄一、概述

移动构造函数、移动赋值函数都是C++11增加的,了解这两个函数之前需要先弄清楚 移动语义右值引用

✨1.1 移动语义

移动语义是C++11才实现的,它的意义是对于一些即将被销毁的对象,可以不复制它,而是窃取它的资源,以此来减少一次复制,达到大幅度提升性能的效果。

在C++11标准之前,没有直接的方法移动对象,即使某些情况不需要拷贝对象,也不得不拷贝。例如,函数返回非引用的返回值时,会先构造一个临时对象作为返回值,函数调用时将该临时对象赋值给接收对象,调用结束后会销毁该临时对象。这里的临时对象是肯定会被销毁的,如果能够将临时对象的资源直接移交给接收对象,那么就可以减少一次拷贝。旧标准则只能拷贝,然后再销毁临时对象的资源。
在这里插入图片描述
C++11支持将即将被销毁的对象移动到接收对象,而不是复制。如下图,临时对象的资源被移动到接收对象,减少一次复制。
在这里插入图片描述
下面代码演示了复制的过程,等学习了下面的移动构造函数就可以编写移动的代码了。

// g++ 14_Copy_Date.cpp -std=c++11
#include <iostream>
#include <stdio.h>
#include <string.h>using namespace std;#define MAX_NEW_MEM		(64*1000*1000)class CDate
{
public:CDate(int year, int mon, int day);	// 构造函数声明CDate(const CDate& date);			// 拷贝构造函数声明~CDate();							// 析构函数声明CDate operator+(int day);			// 加号运算符声明void show(){cout << "Date: " << m_year << "." << m_mon << "." << m_day << ", this=" << this << endl;//cout << "Date: " << str << endl;}private:int m_year;int m_mon;int m_day;char *str;
};// 构造函数定义
CDate::CDate(int year, int mon, int day)
{m_year = year;m_mon = mon;m_day = day;str = new char[MAX_NEW_MEM];sprintf(str, "%4d.%02d.%02d", year,mon,day);cout << "Calling Constructor" << ", this=" << this <<endl;
}// 拷贝构造函数定义
CDate::CDate(const CDate& date)
{m_year = date.m_year;m_mon = date.m_mon;m_day = date.m_day;str = new char[MAX_NEW_MEM];memcpy(str, date.str, MAX_NEW_MEM);cout << "Calling Copy Constructor" << ", this=" << this << ", Copy Data" <<endl;
}// 析构函数定义
CDate::~CDate()
{cout << "Calling Destructor" << ", this=" << this <<endl;delete [] str;
}CDate CDate::operator+(int day)
{CDate temp = *this;temp.m_day += day;cout << "Calling operator+" << ", this=" << &temp << endl;return temp;
}int main()
{CDate date(2024,06,07);cout << endl;CDate date1 = std::move(date+1);// std::move 强制将 date+1 的求值结果转为右值date1.show();cout << endl;return 0;
}

运行结果如下:
1、std::move 强制将 date+1 的求值结果转为右值,避免编译器优化;
2、temp 对象赋值给date1之后,就销毁了,如果可以直接将temp对象的资源给到date1,就可以减少一次复制。
在这里插入图片描述


✨1.2 右值引用

前面介绍了C++11的移动语义,那这个移动语义是怎样实现的呢?

C++11标准引入的一种新的引用类型——右值引用,来实现移动操作。右值引用只能关联到右值,而右值要么是字面值,要么是求值过程中产生的临时对象。右值引用的定义使用了&&,可以参考下面代码:

int &&rri = 13; // 定义了右值引用 rri

上个小节的临时对象只会在程序中很短暂的停留,使用完之后会被立即销毁。C++11标准通过 右值引用 来完全地接管这些临时对象,然后将临时对象的资源 移动 到接收对象,并且要保证这个临时对象可以被正常销毁。这就是C++11标准实现移动语义的原理。

关于左值、右值、右值引用还有疑惑的,可以参考这篇文章:【C++ | 左值、右值】一文了解C++的左值、右值、左值引用(&)、右值引用(&&)


在这里插入图片描述

🎄二、移动构造函数(move constructor)

移动构造函数(move constructor),是C++11标准增加的在创建对象时移动旧对象资源的构造函数。

类的移动构造函数原型通常是这样的:类名(类类型 &&);。以CDate类为例,其移动构造函数声明如下:

CDate(CDate &&date);

移动构造函数的几个特点:
1、函数名和类名相同,没有返回值,因为它也是构造函数的一种;
2、第一个参数必须是一个自身类类型的右值引用(&&),且其他参数都有默认值。
3、第一个参数不能声明为 const 右值引用的原因是该引用在函数内会被修改(移动资源)。
4、移动构造函数执行后,需要保证右值引用的对象能够被正常销毁。

下面看看怎样声明定义自己的移动构造函数:

CDate(CDate &&date) noexcept;	// 声明
CDate::CDate(CDate&& date) noexcept // 实现
{m_year = date.m_year;m_mon = date.m_mon;m_day = date.m_day;str = date.str;date.str = NULL;cout << "Calling Move Constructor" << ", this=" << this <<endl;
}

移动构造函数解析:

  • 1、处理普通数据成员,直接使用旧对象的进行赋值;
  • 2、处理指向堆内存的指针,直接堆内存地址给新对象的指针,旧对象指针指向NULL。
    语句str = date.str;将旧对象的str直接给到正在创建的对象,而不重新new、复制。
    语句date.str = NULL;将旧对象的str赋值为NULL,保证其可以被正常销毁,delete NULL;delete[] NULL不会造成任何问题。
  • 3、由于移动操作“ 窃取” 资源, 它通常不分配任何资源。 因此, 移动操作通常不会抛出任何异常。不抛出异常的函数应该使用 noexcept 通知标准库,避免编译器为了处理异常而作一些额外的工作。

在这里插入图片描述

🎄三、使用移动构造函数

1、使用移动构造函数就需要弄清楚移动构造函数是什么时候会被调用的?
如果使用一个右值(即将被销毁的对象)去初始化同类型的对象,就会调用该类的移动构造函数。

2、怎样的类需要定义移动构造函数?
如果该类的成员存在指针指向new分配的堆内存,则可以跟进需要定义移动构造函数。

3、什么时候编译器会提供默认移动构造函数?
如果该类没有定义拷贝构造函数,且没有定义移动构造函数,则编译器会提供一个 默认的移动构造函数
如果该类定义了拷贝构造函数,则编译器不会提供 默认的移动构造函数,需要用到移动构造的地方都会调用拷贝构造函数。
如果该类定义了移动构造函数,则编译器不会提供 默认的拷贝构造函数,需要用到拷贝构造的时候,编译器会报错。

4、默认的移动构造函数做了什么工作?
默认的移动构造函数 所做的工作与默认拷贝构造函数的工作一样,执行逐成员初始化并复制内置类型。如果成员是类对象,将使用相应类的构造函数和赋值运算符,就像参数为右值一样。

下面是第一小节的代码,加了移动构造函数后的例子:

// g++ 14_Move_Constructor_Date.cpp -std=c++11
#include <iostream>
#include <stdio.h>
#include <string.h>using namespace std;#define MAX_NEW_MEM		(64*1000*1000)class CDate
{
public:CDate(int year, int mon, int day);	// 构造函数声明CDate(const CDate& date);			// 拷贝构造函数声明CDate(CDate&& date) noexcept;		// 移动构造函数声明~CDate();							// 析构函数声明CDate operator+(int day);			// 加号运算符声明void show(){cout << "Date: " << m_year << "." << m_mon << "." << m_day << ", this=" << this << endl;//cout << "Date: " << str << endl;}private:int m_year;int m_mon;int m_day;char *str;
};// 构造函数定义
CDate::CDate(int year, int mon, int day)
{m_year = year;m_mon = mon;m_day = day;str = new char[MAX_NEW_MEM];sprintf(str, "%4d.%02d.%02d", year,mon,day);cout << "Calling Constructor" << ", this=" << this <<endl;
}// 拷贝构造函数定义
CDate::CDate(const CDate& date)
{m_year = date.m_year;m_mon = date.m_mon;m_day = date.m_day;str = new char[MAX_NEW_MEM];memcpy(str, date.str, MAX_NEW_MEM);cout << "Calling Copy Constructor" << ", this=" << this << ", Copy Data" <<endl;
}CDate::CDate(CDate&& date) noexcept
{m_year = date.m_year;m_mon = date.m_mon;m_day = date.m_day;str = date.str;date.str = NULL;cout << "Calling Move Constructor" << ", this=" << this <<endl;
}// 析构函数定义
CDate::~CDate()
{cout << "Calling Destructor" << ", this=" << this <<endl;delete [] str;
}CDate CDate::operator+(int day)
{CDate temp = *this;temp.m_day += day;cout << "Calling operator+" << ", this=" << &temp << endl;return temp;
}int main()
{CDate date(2024,06,07);cout << endl;CDate date1 = std::move(date+1);// std::move 强制将 date+1 的求值结果转为右值date1.show();cout << endl;return 0;
}

运行结果:可以看到相比于第一小节的代码,这里调用了移动构造函数,减少了一次拷贝。
在这里插入图片描述


在这里插入图片描述

🎄四、总结

👉本文介绍C++11的移动构造函数,讲解了为什么需要移动构造函数,怎样声明、定义移动构造函数,怎样使用移动构造函数。

移动构造函数的目的就是为了减少一次拷贝,提升性能。C++11主要通过右值引用来实现移动语义,在使用右值(即将销毁的对象)去创建对象时,调用移动构造函数而不调用拷贝构造函数,以此减少一次拷贝。

在这里插入图片描述
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁

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

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

相关文章

和鲸101计划:Python 气象海洋数据分析 Workshop 回顾

导语&#xff1a;一个科学家最大的价值不是个人取得了多少成绩&#xff0c;而是他培养了多少科学家&#xff0c;他的学生又培养出多少科学家。如果科学精神能从我们这里一代代传承&#xff0c;并且不断推动社会进步&#xff0c;这就是我们此生最大的价值。 ——源自《中国气象…

Day28:回溯法 491.递增子序列 46.全排列 47.全排列 II 332.重新安排行程 51. N皇后 37. 解数独 蓝桥杯 与或异或

491. 非递减子序列 给你一个整数数组 nums &#xff0c;找出并返回所有该数组中不同的递增子序列&#xff0c;递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。 数组中可能含有重复元素&#xff0c;如出现两个整数相等&#xff0c;也可以视作递增序列的一种特殊情…

双指针问题2

文章目录 1. 有效三角形的个数&#xff08;611&#xff09;2. 查找总价格为目标值的两个商品&#xff08;LCR179&#xff09;3. 三数之和&#xff08;15&#xff09;4. 四数之和&#xff08;18&#xff09; 1. 有效三角形的个数&#xff08;611&#xff09; 题目描述&#xff…

逻辑蕴含、函数依赖集的闭包、Armstrong公理、属性集闭包

一、引言 Armstrong公理-从给定的函数依赖集得到关系模式的完整依赖集 二、逻辑蕴含 1、定义 设F是关系模式R上的函数依赖集&#xff0c;X、Y是R的属性子集&#xff0c;对于R的每个满足F的关系实例r&#xff0c;若函数 依赖都成立&#xff0c;则称F逻辑蕴含。 记为&#…

Mamaba3--RNN、状态方程、勒让德多项式

Mamaba3–RNN、状态方程、勒让德多项式 一、简单回顾 在Mamba1和Mamba2中分别介绍了RNN和状态方程。 下面从两个图和两个公式出发&#xff0c;对RNN和状态方程做简单的回顾&#xff1a; R N N : s t W s t − 1 U x t &#xff1b; O t V s t RNN: s_t Ws_{t-1}Ux_t&…

shadertoy-安装和使用

一、安装vscode 安装vscode流程 二、安装插件 1.安装glsl编辑插件 2.安装shader toy插件 三、创建glsl文件 test.glsl文件 float Grid(float size, vec2 fragCoord) {vec2 r fragCoord / size;vec2 grid abs(fract(r - 0.5) - 0.5) / fwidth(r);float line min(grid…

Linux内核开发-编译内核源码

前言 大部分公司的所谓的Linux内核工程师主要工作是基于社区开源内核进行定制化修改&#xff0c;基本不会有机会向上游提供patch&#xff0c;仅限于公司内部业务的修修补补。 作为内核开发工程师两年多&#xff0c;精力一直被公司业务消耗&#xff0c;所有的内核知识都来自于…

异构集成封装类型2D、2.1D、2.3D、2.5D和3D封装技术

异构集成封装类型&#xff1a;2D、2.1D、2.3D、2.5D和3D封装详解 简介随着摩尔定律的放缓&#xff0c;半导体行业越来越多地采用芯片设计和异构集成封装来继续推动性能的提高。这种方法是将大型硅芯片分割成多个较小的芯片&#xff0c;分别进行设计、制造和优化&#xff0c;然后…

【深度学习驱动流体力学】计算流体力学openfoam-paraview与python3交互

目的1:配置 ParaView 中的 Python Shell 和 Python 交互环境 ParaView 提供了强大的 Python 接口,允许用户通过 Python 脚本来控制和操作其可视化功能。在 ParaView 中,可以通过 View > Python Shell 菜单打开 Python Shell 窗口,用于执行 Python 代码。要确保正确配置 …

[Linux] vi编辑器

命令模式&文本模式 命令模式就输入命令然后执行&#xff0c;文本模式就是系统把你的输入都当成写进文件里的字符 切换模式&#xff1a; 刚进入默认是命令模式&#xff0c;按: i I a A o O 进入文本模式&#xff0c; 通过他们进入文本模式有什么不同&#xff1f; 然后按esc进…

python 版本切换,更换当前默认版本

电脑可以安装多个版本&#xff0c;但是好像没有正规的维护python版本的工具&#xff0c;比如前端就有nvm切换node版本&#xff0c;但是python我没找到比较好的&#xff08;有大佬知道路过方便留言一下&#xff0c;跪谢。。&#xff09; 废话不多说&#xff0c;更改默认版本很简…

DIVE INTO DEEP LEARNING 36-49

文章目录 36. Data augmentation36.1 Training with enhanced data36.2 Enhancement measures36.3 Data augmentation summary 37. Fine tuning37.1 Fine tuning Introduce37.2 Fine tuning Step37.3 Fine tuning summary 38. Object detection38.1 Object detection38.2 Edge …

[Linux] Shell

chsh不是一种sh,而是一个命令行使用程序&#xff0c;用于更改默认shell CentOS是个开源软件&#xff0c;没有sh,sh是商业版的&#xff0c; 按ls /bin/*sh显示的sh实际上是个链接文件&#xff0c;连接的bash 在命令行输入新的sh名&#xff0c;会启动一个新的进程&#xff0c; 输…

厂里资讯之app端文章搜索

app端文章搜索 1) 内容介绍 文章搜索 ElasticSearch环境搭建 索引库创建 文章搜索多条件复合查询 索引数据同步 搜索历史记录 Mongodb环境搭建 异步保存搜索历史 查看搜索历史列表 删除搜索历史 联想词查询 联想词的来源 联想词功能实现 2) 搭建ElasticSearch环境 …

MyBatis系列七: 一级缓存,二级缓存,EnCache缓存

缓存-提高检索效率的利器 官方文档 一级缓存基本介绍快速入门Debug一级缓存执行流程一级缓存失效分析 二级缓存基本介绍快速入门Debug二级缓存执行流程注意事项和使用细节 mybatis的一级缓存和二级缓存执行顺序小实验细节说明 EnCache缓存基本介绍配置和使用EhCache细节说明 My…

SpringBoot整合Minio(支持公有及私有bucket)

&#x1f60a; 作者&#xff1a; 一恍过去 &#x1f496; 主页&#xff1a; https://blog.csdn.net/zhuocailing3390 &#x1f38a; 社区&#xff1a; Java技术栈交流 &#x1f389; 主题&#xff1a; SpringBoot整合Minio(支持公有及私有bucket) ⏱️ 创作时间&#xff1…

张大哥笔记:如何选择一个人就值得做的副业

很多人喜欢把上班称为主业&#xff0c;把上班之外的工作称为副业&#xff0c;不管以哪种方式称呼都可以&#xff0c;只要能赚钱就行&#xff0c;上班的本质就是出卖时间&#xff0c;不管你是月入5000还是月入2万&#xff0c;都是给老板打工&#xff01; 但搞笑的就是月入2万的人…

关于app爬虫的环境准备

摘要 有些数据需要在手机应用中才能查看&#xff0c;没有网页版&#xff0c;所以学习移动端的爬虫是有必要的。 手机系统分为安卓和苹果两大系统&#xff0c;本次讲解主要以安卓手机为例 有安卓手机的可以使用手机&#xff0c;没有的可以使用模拟器&#xff0c;本次以夜神模…

基于C++、MFC和Windows套接字实现的简单聊天室程序开发

一、一个简单的聊天室程序 该程序由服务器端和客户端两个项目组成&#xff0c;这两个项目均基于对话框的程序。服务器端项目负责管理客户端的上线、离线状态&#xff0c;以及转发客户端发送的信息。客户端项目则负责向服务器发送信息&#xff0c;并接收来自服务器的信息&#…

[机器学习算法]决策树

1. 理解决策树的基本概念 决策树是一种监督学习算法&#xff0c;可以用于分类和回归任务。决策树通过一系列规则将数据划分为不同的类别或值。树的每个节点表示一个特征&#xff0c;节点之间的分支表示特征的可能取值&#xff0c;叶节点表示分类或回归结果。 2. 决策树的构建…