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

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

前言


在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

befd64bf89024fc6a48f686b26b61b9e.png


二、移动赋值操作符(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


33f675b5d7264348953826287ea4074c.png


总结


当我们需要在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引入的重要特性,通过资源所有权的转移而不是数据的拷贝,提高了程序的性能和效率。对于处理大量数据或动态分配的对象,使用移动操作可以显著减少资源的复制和分配,从而提升程序的效率。然而,在使用移动操作时需要注意管理资源的责任,并确保对象的移后状态仍然有效和可用。

相关文章
|
1月前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
90 5
|
1月前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
81 4
|
2月前
|
算法 数据挖掘 Shell
「毅硕|生信教程」 micromamba:mamba的C++实现,超越conda
还在为生信软件的安装配置而烦恼?micromamba(micromamba是mamba包管理器的小型版本,采用C++实现,具有mamba的核心功能,且体积更小,可以脱离conda独立运行,更易于部署)帮你解决!
78 1
|
2月前
|
存储 C++
c++的指针完整教程
本文提供了一个全面的C++指针教程,包括指针的声明与初始化、访问指针指向的值、指针运算、指针与函数的关系、动态内存分配,以及不同类型指针(如一级指针、二级指针、整型指针、字符指针、数组指针、函数指针、成员指针、void指针)的介绍,还提到了不同位数机器上指针大小的差异。
62 1
|
2月前
|
Linux C语言 C++
vsCode远程执行c和c++代码并操控linux服务器完整教程
这篇文章提供了一个完整的教程,介绍如何在Visual Studio Code中配置和使用插件来远程执行C和C++代码,并操控Linux服务器,包括安装VSCode、安装插件、配置插件、配置编译工具、升级glibc和编写代码进行调试的步骤。
368 0
vsCode远程执行c和c++代码并操控linux服务器完整教程
|
2月前
|
C++
C++构造函数初始化类对象
C++构造函数初始化类对象
25 0
|
2月前
|
C++
C++入门4——类与对象3-2(构造函数的类型转换和友元详解)
C++入门4——类与对象3-2(构造函数的类型转换和友元详解)
29 0
|
26天前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
44 2
|
1月前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
90 4
|
2月前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
31 4