【C++11保姆级教程】移动构造函数(move constructor)和移动赋值操作符(move assignment operator)

news2025/1/12 11:57:54

文章目录

  • 前言
  • 一、移动构造函数(Move Constructor)
    • 1.1 移动构造函数是什么?
    • 1.2 基本格式
    • 1.3 示例代码
    • 1.4 输出结果
  • 二、移动赋值操作符(Move Assignment Operator)
    • 2.1 移动赋值操作符是什么?
    • 2.2 一般格式
    • 2.3 输出
  • 总结


前言

在C++11标准中引入了移动语义的概念,通过移动构造函数和移动赋值操作符,我们可以更高效地管理对象的资源。本文将以通俗易懂的方式详细解释移动构造函数和移动赋值操作符的概念,并通过生动的比喻帮助读者更好地理解这两个概念。


一、移动构造函数(Move Constructor)

1.1 移动构造函数是什么?

移动构造函数是一个特殊的构造函数,它能够从一个右值引用(rvalue reference)创建新的对象,而无需进行深拷贝(deep copy)。我们可以将移动构造函数比喻成搬家时的快递员。

假设你搬家,有一堆家具需要装进卡车。传统的深拷贝(复制构造函数)就像是你把每一件家具都精心地复制一份,然后放进卡车上。这个过程费时费力,而且你原本的家具还要保留。

但是,如果你找来一位勇敢的快递员(移动构造函数),他们可以直接将你的家具移动到新的屋子里,而不用复制。这样,节省了时间和精力,而且你原本的家具可以顺利放进新的屋子。

在代码中,移动构造函数使用右值引用作为参数,并且我们将原始对象的资源直接转移到新对象中,而不是进行复制。这就如同快递员将家具从旧的房子搬到新的房子中,节省了时间和空间开销。

总的来说:可以理解为快速的拷贝构造函数

1.2 基本格式

在构造函数后面写noexcept

ClassName(ClassName&& other) noexcept
{
    // Move the resources from 'other' to the new object
    // ...
}

1.3 示例代码

#include <iostream>

class MyObject {
private:
    int* data;

public:
    MyObject() : data(nullptr) {
        std::cout << "Default Constructor" << std::endl;
    }

    MyObject(int value) : data(new int(value)) {
        std::cout << "Regular Constructor" << std::endl;
    }

    // 移动构造函数
    MyObject(MyObject&& other)noexcept : data(other.data) {
        other.data = nullptr;
        std::cout << "Move Constructor" << std::endl;
    }

    ~MyObject() {
        delete data;
        std::cout << "Destructor" << std::endl;
    }

    void printData() const {
        if (data != nullptr) {
            std::cout << "Data: " << *data << std::endl;
        } else {
            std::cout << "Data is null" << std::endl;
        }
    }
};

int main() {
    MyObject obj1(10);
    obj1.printData();

    MyObject obj2(std::move(obj1));  // 使用std::move调用移动构造函数
    obj2.printData();
    
    obj1.printData();  // obj1的data现在为null

    return 0;
}

1.4 输出结果

Regular Constructor
Data: 10
Move Constructor
Data: 10
Data is null
Destructor
Destructor

在这里插入图片描述

二、移动赋值操作符(Move Assignment Operator)

2.1 移动赋值操作符是什么?

移动赋值操作符允许我们将一个对象的资源转移到另一个对象上。可以把移动赋值操作符比喻成两个人交换工作岗位。

想象一下,你在一家公司工作,有一天你被调往另外一个部门。传统的方式是,你将自己的工作内容复制一份,再将新工作的内容复制回来,形成了两份一样的工作内容。这样的操作显然很冗余。

然而,通过移动赋值操作符,你可以直接将自己的工作内容交给新的员工,并且接管他们原本的工作,省去了不必要的复制步骤。

在代码中,移动赋值操作符也使用右值引用作为参数。我们将源对象的资源直接转移到目标对象中,同时将源对象恢复到一种可安全销毁或重新赋值的状态。这就如同两个人交换工作岗位,互相拥有对方的资源和责任。

总的来说:可以理解为快速的赋值运算符。(比一般的更快,因为无需深拷贝)

2.2 一般格式

ClassName& operator=(ClassName&& other) noexcept
{
    // Move the resources from 'other' to the current object
    // ...
    return *this;
}

示例代码:

#include <iostream>

class MyObject {
private:
    int* data;

public:
    MyObject() : data(nullptr) {
        std::cout << "Default Constructor" << std::endl;
    }

    MyObject(int value) : data(new int(value)) {
        std::cout << "Regular Constructor" << std::endl;
    }

    // 移动构造函数
    MyObject(MyObject&& other) noexcept : data(other.data) {
        other.data = nullptr;
        std::cout << "Move Constructor" << std::endl;
    }

    // 移动赋值操作符
    MyObject& operator=(MyObject&& other) noexcept {
        if (this != &other) {
            delete data;
            data = other.data;
            other.data = nullptr;
        }
        std::cout << "Move Assignment Operator" << std::endl;
        return *this;
    }

    ~MyObject() {
        delete data;
        std::cout << "Destructor" << std::endl;
    }

    void printData() const {
        if (data != nullptr) {
            std::cout << "Data: " << *data << std::endl;
        } else {
            std::cout << "Data is null" << std::endl;
        }
    }
};

int main() {
    MyObject obj1(10);
    obj1.printData();

    MyObject obj2;
    obj2 = std::move(obj1);  // 使用std::move调用移动赋值操作符
    obj2.printData();

    obj1.printData();  // obj1的data现在为null

    return 0;
}

2.3 输出

Regular Constructor
Data: 10
Default Constructor
Move Assignment Operator
Data: 10
Data is null
Destructor

在这里插入图片描述


总结

当我们需要在C++中处理大量数据或动态分配的对象时,移动构造函数和移动赋值操作符成为了重要的工具。它们是C++11引入的特性,旨在提高程序的性能和效率。
移动构造函数(move constructor)和移动赋值操作符(move assignment operator)的作用是允许将临时对象或资源所有权从一个对象转移给另一个对象,而无需执行深层的数据拷贝和分配新资源。相比复制构造函数和复制赋值操作符,移动操作通常更加高效,因为它只需要重新指定资源的所有权关系,而不需要执行资源的复制或分配。
移动构造函数的语法如下:

类名(类名&amp;&amp; other) noexcept
{
    // 进行资源所有权的转移
}

移动赋值操作符的语法如下:

类名&amp; operator=(类名&amp;&amp; other) noexcept
{
    if (this != &amp;other) {
        // 进行资源所有权的转移
    }
    return *this;
}

在移动构造函数和移动赋值操作符中,我们通过使用右值引用(&&)来标识移动语义,并使用std::move()函数将对象转换为右值。
使用移动构造函数和移动赋值操作符的好处包括:

1.减少不必要的数据拷贝和资源分配,提高程序的性能和效率。
2.在处理大型对象或大量数据时,减少内存的占用和提高程序的响应速度。
3.支持对不可拷贝的对象进行移动操作,使得这些对象也可以被移动和管理。

在使用移动操作时,需要注意以下几点:

4.移动构造函数和移动赋值操作符通常会将被移动对象的资源指针设置为nullptr,以避免资源的重复释放。
5.移动构造函数和移动赋值操作符通常应该具有noexcept规定,表示它们不会抛出异常。
6.移动操作并不会自动删除或释放资源,只是转移资源的所有权关系。移动后的对象需要负责管理和释放资源。移动后的源对象状态通常不可预测,应当谨慎使用。
7.使用移动操作时,对象的移后状态应该仍然是有效且可用的。

总而言之,移动构造函数和移动赋值操作符是C++11引入的重要特性,通过资源所有权的转移而不是数据的拷贝,提高了程序的性能和效率。对于处理大量数据或动态分配的对象,使用移动操作可以显著减少资源的复制和分配,从而提升程序的效率。然而,在使用移动操作时需要注意管理资源的责任,并确保对象的移后状态仍然有效和可用。

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

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

相关文章

SpringBoot + Vue 微人事项目(第二天)

昨天做了微人事登录的前端页面和后端接口&#xff0c;实现了前后端接口的对接&#xff0c;输入正确的用户名和密码之后&#xff0c;成功的跳转到home页。现在要做的就是Home页的Title制作 Home页的title制作 使用Element UI中的Container布局容器 复制的代码如下&#xff0c…

redis十种数据类型及底层原理

概述 Redis 是一个开源的高性能键值数据库&#xff0c;它支持多种数据类型&#xff0c;可以满足不同的业务需求。本文将介绍 Redis 的10种数据类型&#xff0c;分别是 string&#xff08;字符串&#xff09; hash&#xff08;哈希&#xff09; list&#xff08;列表&#xf…

【C++】函数指针

2023年8月18日&#xff0c;周五上午 今天在B站看Qt教学视频的时候遇到了 目录 语法和typedef或using结合我的总结 语法 返回类型 (*指针变量名)(参数列表)以下是一些示例来说明如何声明不同类型的函数指针&#xff1a; 声明一个不接受任何参数且返回void的函数指针&#xf…

73 【转载】关于USB最大传输速率的文章记录

1 引言 本文章记录了关于USB最大传输速率内容&#xff0c;资料转载自其它博客&#xff0c;在此记录只是便于日常查询。 2 转载文章 原文链接&#xff1a;【USB笔记】USB2.0 不同传输类型下的理论最大速率_usb2.0速率_dadalaohua的博客-CSDN博客 3 USB Bulk传输速率限制 以下是…

「UG/NX」Block UI 指定点SpecifyPoint

✨博客主页何曾参静谧的博客📌文章专栏「UG/NX」BlockUI集合📚全部专栏「UG/NX」NX二次开发「UG/NX」BlockUI集合「VS」Visual Studio「QT」QT5程序设计「C/C+&#

SSL证书产品简介

SSL证书是什么&#xff1f; 目前互联网常用的HTTP协议是非常不安全的明文传输协议。而SSL&#xff08;安全套接字层&#xff0c;Secure Sockets Layer&#xff09;及其继任者TLS(传输层安全协议&#xff0c;Transport Layer Security)&#xff0c;是一种实现网络通信加密的安全…

Linux实用运维脚本分享

Linux实用运维脚本分享&#x1f343; MySQL备份 目录备份 PING查询 磁盘IO检查 性能相关 进程相关 javadump.sh 常用工具安装 常用lib库安装 系统检查脚本 sed进阶 MySQL备份 #!/bin/bashset -eUSER"backup" PASSWORD"backup" # 数据库数据目录…

【Linux旅行记】进度条小程序

文章目录 一、预备知识1.1回车换行1.2缓冲区 二、倒计时三、进度条3.1普通版本源代码3.2高级版本源代码 &#x1f340;小结&#x1f340; &#x1f389;博客主页&#xff1a;小智_x0___0x_ &#x1f389;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &…

数据可视化工具CEETRON Envision助力ESTECO客户实现CAE数据转化

​行业&#xff1a;制造业&#xff1b;工业设计&#xff1b;汽车&#xff1b;航天 挑战&#xff1a;工业客户需要有效的方法来解释 CAE 数据&#xff1b;ESTECO 寻求提供 CAE 可视化功能来帮助客户做出决策&#xff1b;许多可用的可视化工具无法提供对模型中数据的完全访问以进…

java练习5 爬楼梯

题目:小谢 喜欢爬楼梯,有时一次爬1阶,有时一次爬2阶. 如果,楼梯有20阶,小明一共有多少次爬法. public static void main(String[] args) {System.out.println(getNumber(20));}public static int getNumber(int n){if(n1){return 1;}if (n2){return 2;}return getNumber(n-1)ge…

Docker vs. Kubernetes:选择合适的场景

在决定使用 Docker 还是 Kubernetes 之前&#xff0c;让我们看看一些实际的场景&#xff0c;以便更好地理解它们的适用性。 使用 Docker 的场景 假设您正在开发一个微服务应用程序&#xff0c;其中每个微服务都需要一些特定的依赖项和环境。在这种情况下&#xff0c;Docker 是一…

218、仿真-基于51单片机步进电机正反转加减速度LCD1602显示Proteus仿真设计(程序+Proteus仿真+配套资料等)

毕设帮助、开题指导、技术解答(有偿)见文未 目录 一、硬件设计 二、设计功能 三、Proteus仿真图​编辑 四、程序源码 资料包括&#xff1a; 需要完整的资料可以点击下面的名片加下我&#xff0c;找我要资源压缩包的百度网盘下载地址及提取码。 方案选择 单片机的选择 方…

shell脚本三剑客之awk

awk文本三剑客之一&#xff0c;是功能最强大的文本工具 awk也是按行来进行操作&#xff0c;对行操作完之后&#xff0c;可以根据指定命令来对行取列 awk的分隔符&#xff1a;默认分隔符是空格或者tab键&#xff0c;多个空格&#xff0c;会自动压缩成一个 用法&#xff1a; …

iPhone上的个人热点丢失了怎么办?如何修复iPhone上不见的个人热点?

个人热点功能可将我们的iPhone手机转变为 Wi-Fi 热点&#xff0c;有了Wi-Fi 热点后就可以与附近的其他设备共享其互联网连接。 一般情况下&#xff0c;个人热点打开就可以使用&#xff0c;但也有部分用户在升级系统或越狱后发现 iPhone 的个人热点消失了。 iPhone上的个人热点…

SpringBoot + Vue 微人事(十二)

职位批量删除实现 编写后端接口 PositionController DeleteMapping("/")public RespBean deletePositionByIds(Integer[] ids){if(positionsService.deletePositionsByIds(ids)ids.length){return RespBean.ok("删除成功");}return RespBean.err("删…

11. Vuepress2.x 关闭夜间模式

修改 docs/.vuepress/config.ts 配置文件 设置 themeConfig.darkMode属性详见 官网 module.exports {host: localhost, // ipport: 8099, //端口号title: 我的技术站, // 设置网站标题description: 描述&#xff1a;我的技术站,base: /, //默认路径head: [// 设置 favor.ico&a…

LVGL基本控件介绍

1. 弧(lv_arc) 特点 弧的0度在右边&#xff0c;90度在下边 效果图 源码 void lv_arc_demo(void) {/* Create an Arc */lv_obj_t* arc lv_arc_create(lv_scr_act(), NULL);/* Set Background range */lv_arc_set_bg_angles(arc, 0, 360);/* Set Forward range */lv_arc_set…

探索心律失常:病因、诊断与治疗以及与肠道菌群的关联

谷禾健康 你是否有时会感到心悸、心慌、胸闷、气短、头晕、乏力&#xff1f;你是否有时感觉自己的心跳过快或过慢&#xff1f; 如果有上述情况&#xff0c;就要引起重视了&#xff0c;你可能存在心律失常。心律失常是最常见的心脏疾病之一&#xff0c;涉及到心脏的电活动节奏异…

JAVA设计模式总结之23种设计模式

一、什么是设计模式 设计模式&#xff08;Design pattern&#xff09;是一套被反复使用、多数人知晓的、经过分类编目的、代码设计…

c++ | 字节转换 | 字长 | 机器位数

为什么有的时候脑子转不过来&#xff1f;&#xff1f; 为什么要对字节、机器长啊、位啊都要门清 位数 一般的就是指计算机的位数&#xff0c;比如64位/32位&#xff0c;更简单的理解&#xff0c;计算机就是在不停的做二进制的计算&#xff0c;比如32位计算机&#xff0c;在长…