C++中的移动构造函数及move语句示例详解

C++中的移动构造函数及move语句示例详解

什么是移动构造函数?

移动构造函数是C++11中新增的一种特殊的构造函数,用于在对象的移动语义下构造新对象。在C++中,移动构造函数的函数名为“移动构造函数”,使用特定的语法和方法来定义。对象在移动语义下被移动时,移动构造函数会被自动调用,其中源对象的数据块会被转移,并被用于新的对象的构造中。

移动构造函数通常用于具有大量内存使用的对象,如字符串、容器等。通过移动构造函数,避免不必要的内存拷贝操作,提高程序性能。

移动构造函数的格式如下:

class ClassName {
  public:
    ClassName(ClassName&&); // 移动构造函数
};

其中 ClassName&& 表示右值引用,用于接收源对象。在移动构造函数中,需要利用其他函数如 std::move() 来实现移动语义下的对象转移。

什么是move语句?

move语句是C++11中新增的标准库函数,在实现移动语义下的对象转移时非常有用。move语句本质上是一种类型转换操作,用于将左值类型的对象转换为右值引用类型的对象,从而实现移动语义下的对象转移。move语句的格式如下:

std::move(x);

其中 x 表示左值对象, std::move() 表示将 x 转换为右值引用类型的对象。注意,调用 std::move() 函数后,原对象的值可能会被修改或破坏。

示例1:移动构造函数使用

下面是一个字符串类 String 的示例,其中定义了移动构造函数:

#include <iostream>
#include <string.h>

class String {
  private:
    char* m_data;
    int m_length;

  public:
    String(const char* data) {
        m_length = strlen(data);
        m_data = new char[m_length + 1];
        strcpy(m_data, data);
    }

    // 移动构造函数
    String(String&& other) : m_data(nullptr), m_length(0) {
        std::cout << "Move constructor is called!" << std::endl;
        m_data = other.m_data;
        m_length = other.m_length;
        other.m_data = nullptr;
        other.m_length = 0;
    }

    ~String() {
        delete[] m_data;
    }

    const char* GetData() const {
        return m_data;
    }
};

int main() {
    String str1("Hello, world!");
    String str2(std::move(str1)); // 使用移动构造函数

    std::cout << "str1 data: " << str1.GetData() << std::endl; // 输出为空指针
    std::cout << "str2 data: " << str2.GetData() << std::endl; // 输出 "Hello, world!"
    return 0;
}

在上面的示例中,我们定义了一个字符串类 String,其中定义了一个移动构造函数。在 main() 函数中,我们创建了 str1str2 两个字符串对象,然后使用 std::move() 函数来将 str1 转换为右值引用,并将其作为参数传递给 str2 的构造函数。在这个过程中,移动构造函数被自动调用,完成了源对象到新对象的数据转移。

需要注意的是,在移动构造函数中,源对象的数据被转移到了新对象中,并且源对象的指针被置为 nullptr。这是为了防止源对象被误用。因此,在 main() 函数中,输出 str1 的数据时,输出为空指针。而输出 str2 的数据时,输出的是源对象的数据,即 "Hello, world!"。

示例2:move语句的使用

下面是一个容器类模板 Array 的示例,其中使用了 std::move() 函数:

#include <iostream>
#include <utility>

template <typename T>
class Array {
  private:
    T* m_data;
    size_t m_size;

  public:
    explicit Array(size_t size) : m_data(new T[size]), m_size(size) {}

    Array(const Array& other) : m_data(new T[other.m_size]), m_size(other.m_size) {
        for (size_t i = 0; i < m_size; i++) {
            m_data[i] = other.m_data[i];
        }
    }

    Array(Array&& other) : m_data(nullptr), m_size(0) {
        std::cout << "Move constructor is called!" << std::endl;
        m_data = other.m_data;
        m_size = other.m_size;
        other.m_data = nullptr;
        other.m_size = 0;
    }

    ~Array() {
        delete[] m_data;
    }

    T& operator[](size_t index) {
        return m_data[index];
    }

    const T& operator[](size_t index) const {
        return m_data[index];
    }

    size_t Size() const {
        return m_size;
    }
};

int main() {
    Array<int> arr1(10);
    for (size_t i = 0; i < arr1.Size(); i++) {
        arr1[i] = i;
    }

    Array<int> arr2(std::move(arr1)); // 使用std::move()

    std::cout << "arr1 size: " << arr1.Size() << std::endl; // 输出 0
    std::cout << "arr2 size: " << arr2.Size() << std::endl; // 输出 10

    for (size_t i = 0; i < arr2.Size(); i++) {
        std::cout << arr2[i] << " ";
    }
    std::cout << std::endl;
    return 0;
}

在上面的示例中,我们定义了一个容器类模板 Array,其中包含了一个移动构造函数。在 main() 函数中,我们创建了 arr1arr2 两个 Array 类型的对象,然后使用 std::move() 函数将 arr1 转换为右值引用,并将其作为参数传递给 arr2 的构造函数。

需要注意的是,在 Array 类型的移动构造函数中,我们使用了 std::move() 函数来将源对象的 m_data 指针转换为右值引用类型。如此一来,源对象的 m_data 指针就被移动到了新对象中,避免了不必要的内存拷贝操作。

main() 函数中,输出 arr1arr2 的大小可以看到,移动构造函数实现了源对象到新对象的数据转移,同时避免了不必要的内存拷贝操作。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++中的移动构造函数及move语句示例详解 - Python技术站

(0)
上一篇 2023年6月26日
下一篇 2023年6月26日

相关文章

  • 解析C#自定义控件的制作与使用实例的详解

    解析C#自定义控件的制作与使用实例的详解 什么是自定义控件 自定义控件是指基于原有控件进行继承、扩展、封装的新型控件。自定义控件可以满足细分领域的需求,提高代码复用性和可维护性,也可以大大提高开发效率。 制作自定义控件的步骤 新建Windows Forms控制台应用程序。 选择项目,右键菜单中“添加”→ “用户控件” → “Inherited Control…

    other 2023年6月25日
    00
  • C/C++中的名字空间与作用域示例详解

    C/C++中的命名空间与作用域示例详解 命名空间(Namespace)是C++中用来避免命名冲突的一种机制,它可以将全局作用域划分为不同的区域,每个区域可以有自己的变量、函数和类等。本文将详细讲解C/C++中的命名空间与作用域,并提供两个示例说明。 1. 命名空间的定义与使用 命名空间的定义使用关键字namespace,后跟命名空间的名称和一对花括号。在命名…

    other 2023年8月19日
    00
  • Mybatis中的config.xml配置文件详细解析

    Mybatis是一款非常流行的ORM框架,它的核心思想是将数据库操作映射成为Java方法,让开发者可以更加专注于业务逻辑的实现。而Mybatis的配置文件config.xml则是整个框架的重要组成部分,本文将对其进行一一讲解。 整体结构 Mybatis的config.xml配置文件整体结构非常简洁,分为configuration、properties、typ…

    other 2023年6月25日
    00
  • php使用mysqli和pdo扩展,测试对比连接mysql数据库的效率完整示例

    当涉及到使用PHP的mysqli和PDO扩展来连接MySQL数据库并测试其效率时,以下是一个完整的攻略,其中包含两个示例说明: 1. mysqli扩展示例 // 创建mysqli连接 $mysqli = new mysqli(\"localhost\", \"username\", \"password\&q…

    other 2023年10月18日
    00
  • win7系统如何批量修改文件和文件夹权限右键没有安全选项卡

    如果在Windows 7系统中需要批量修改文件或文件夹的权限,但是发现右键菜单中没有“安全”选项卡,那么可以按照以下步骤来解决: 方法一:通过组策略编辑器来添加安全选项卡 以管理员身份打开“组策略编辑器”(gpedit.msc); 在“计算机配置”——“管理模板”——“Windows组件”下找到“Windows资源管理器”; 右侧窗口双击“阻止访问网络位置中…

    other 2023年6月27日
    00
  • Win10系统自带加密文件夹的两种方法

    下面是Win10系统自带加密文件夹的两种方法的完整攻略: 方法一:使用Windows系统自带的文件夹加密功能 找到要加密的文件夹,右键点击进入属性,选择“高级”选项卡。 勾选“加密内容以保护数据”,点击确定保存设置。 系统会弹出对话框询问是否将加密应用于该文件夹及其子文件夹和文件,选择“确定”。 当你再次打开该文件夹时,会发现它的名称变成了绿色,表示已加密。…

    other 2023年6月27日
    00
  • 利用C++ R3层断链实现模块隐藏功能

    利用C++ R3层断链实现模块隐藏功能可以通过操作Windows系统内核模块,使得应用程序在加载模块的时候不出现在模块列表中,从而实现模块的隐藏。 下面是具体的操作步骤: 第一步:获取模块基址 获取需要隐藏的模块的基址。可以使用工具如Process Hacker或Task Manager等查看正在运行的进程,并获取该进程中需要隐藏的模块的基址。可以使用函数G…

    other 2023年6月27日
    00
  • 卸载postgresql数据库

    卸载PostgreSQL数据库的完整攻略,过程中至少包含两条示例说明。 以下是卸载PostgreSQL数据库的完整攻略,包括以下步骤: 停止PostgreSQL服务 卸载PostgreSQL软件 删除PostgreSQL数据目录 删除PostgreSQL用户和组 示例说明 步骤一:停止PostgreSQL服务 在卸载PostgreSQL之前,需要先停止Pos…

    other 2023年5月9日
    00
合作推广
合作推广
分享本页
返回顶部