c++ 移动赋值/移动构造函数的实现

 更新时间:2026年03月27日 09:18:27   作者:.select.  
这篇文章主要介绍了c++ 移动赋值/移动构造函数的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

在 C++11 引入移动语义(Move Semantics)之前,对象之间的赋值或初始化通常涉及深拷贝(Deep Copy),即复制所有数据。这对于包含动态分配资源(如 std::vectorstd::string, 原始指针管理的内存)的对象来说,开销很大。

移动构造函数移动赋值运算符允许我们将资源的“所有权”从一个对象转移到另一个对象,而不是复制数据。这极大地提高了性能,特别是对于临时对象(右值)。

1. 核心概念:左值 vs 右值

理解移动语义的前提是区分左值(lvalue)和右值(rvalue):

  • 左值:有名字、有持久地址的对象(如 int a = 10; 中的 a)。
  • 右值:通常是临时对象、字面量或即将销毁的对象(如 a + b 的结果,或者函数返回的非引用对象)。

移动语义的核心思想是:既然右值(临时对象)马上就要销毁了,我们没必要复制它的资源,直接“偷”过来用就行了。

2. 移动构造函数 (Move Constructor)

定义:当一个新对象被一个右值初始化时调用。
签名ClassName(ClassName&& other)
参数:接受一个右值引用(T&&)。

作用

将源对象(other)的资源指针直接转移给新对象,并将源对象的指针置为 nullptr(或其他安全状态),防止析构时重复释放内存。

代码示例

class MyVector {
private:
    int* data;
    size_t size;
public:
    // 构造函数
    MyVector(size_t s) : size(s) {
        data = new int[s];
        std::cout << "构造: 分配内存\n";
    }
    // 拷贝构造函数 (深拷贝)
    MyVector(const MyVector& other) : size(other.size) {
        data = new int[size];
        std::copy(other.data, other.data + size, data);
        std::cout << "拷贝构造: 深拷贝内存\n";
    }
    // --- 移动构造函数 ---
    MyVector(MyVector&& other) noexcept 
        : data(other.data), size(other.size) {
        // 关键步骤:窃取指针
        other.data = nullptr; // 将源对象置为空,防止其析构时释放这块内存
        other.size = 0;
        std::cout << "移动构造: 转移所有权\n";
    }
    // 析构函数
    ~MyVector() {
        if (data) {
            delete[] data;
            std::cout << "析构: 释放内存\n";
        }
    }
};
// 使用场景
MyVector createVector() {
    MyVector temp(100);
    return temp; // 返回临时对象(右值)
}
int main() {
    // 这里会触发移动构造函数,而不是拷贝构造
    MyVector v = createVector(); 
    return 0;
}

3. 移动赋值运算符 (Move Assignment Operator)

定义:当一个已存在的对象被赋予一个右值时调用。
签名ClassName& operator=(ClassName&& other)
返回值:通常返回 *this 以支持链式赋值。

作用

  1. 自赋值检查:防止 a = std::move(a) 导致错误。
  2. 清理旧资源:释放当前对象持有的旧资源。
  3. 窃取新资源:接管源对象的资源。
  4. 重置源对象:将源对象置于有效但未指定的状态(通常指针置空)。

代码示例

class MyVector {
    // ... (同上) ...
public:
    // --- 移动赋值运算符 ---
    MyVector& operator=(MyVector&& other) noexcept {
        if (this != &other) { // 1. 自赋值检查
            // 2. 释放当前资源
            delete[] data;
            // 3. 窃取资源
            data = other.data;
            size = other.size;
            // 4. 重置源对象
            other.data = nullptr;
            other.size = 0;
        }
        std::cout << "移动赋值: 转移所有权\n";
        return *this;
    }
};
int main() {
    MyVector v1(10);
    MyVector v2(20);
    // std::move 将左值 v1 强制转换为右值引用,触发移动赋值
    v2 = std::move(v1); 
    // 此时 v1 内部指针为 null,v2 拥有了原本 v1 的内存
    return 0;
}

4. 关键细节与最佳实践

A.noexcept的重要性

移动构造函数和移动赋值运算符必须标记为 noexcept(除非你真的可能抛出异常)。

  • 原因:标准库容器(如 std::vector)在扩容重新分配内存时,如果元素的移动操作是 noexcept 的,它会优先使用移动;否则,为了保证异常安全(如果移动中途失败,原数据还在),它只能退回到拷贝
  • 如果不加 noexceptstd::vector<MyVector> 的性能可能会退化回拷贝语义。

B.std::move是什么?

std::move 不移动任何东西。它只是一个强制类型转换工具,将左值转换为右值引用(T&&),从而告诉编译器:“这个对象我可以被移动,请调用移动版本的操作”。

  • 调用 std::move(x) 后,x 处于有效但未指定的状态。除了销毁或重新赋值外,不应再使用 x 的值。

C. 规则之五 (Rule of Five)

如果你需要自定义以下五个函数中的任何一个,通常意味着你需要自定义全部五个:

  1. 析构函数
  2. 拷贝构造函数
  3. 拷贝赋值运算符
  4. 移动构造函数
  5. 移动赋值运算符

如果你只定义了移动操作而没有定义拷贝操作,编译器会自动删除默认的拷贝构造和拷贝赋值函数(因为资源已经被移走了,默认拷贝是不安全的)。

D. 成员变量的自动移动

如果你的类成员变量本身支持移动语义(如 std::unique_ptrstd::vectorstd::string),你可以使用 default 让编译器自动生成高效的移动操作:

class MyClass {
    std::vector<int> vec;
    std::string name;
    // 编译器自动生成的移动构造/赋值会分别调用 vec 和 name 的移动操作
    // 非常高效且安全
    MyClass(MyClass&&) = default; 
    MyClass& operator=(MyClass&&) = default;
};

总结对比

特性拷贝构造/赋值移动构造/赋值
参数类型const T& (左值引用)T&& (右值引用)
行为深度复制数据 (Deep Copy)转移资源所有权 (Steal Pointers)
源对象状态保持不变变为有效但未指定状态 (通常为空)
性能较慢 (涉及内存分配和复制)极快 (仅指针赋值)
适用场景需要保留源对象数据时源对象是临时值或不再需要时

到此这篇关于c++ 移动赋值/移动构造函数的实现的文章就介绍到这了,更多相关c++ 移动赋值/移动构造内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++实现查询本机信息的示例代码

    C++实现查询本机信息的示例代码

    这篇文章主要为大家详细介绍了如何利用C++实现查询本机信息,并且进行上报,文中的示例代码讲解详细,具有一定的参考价值,感兴趣的可以了解一下
    2023-05-05
  • C++超集C++/CLI模块的基本类型

    C++超集C++/CLI模块的基本类型

    这篇文章介绍了C++超集C++/CLI模块的基本类型,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-07-07
  • C++发邮件简单实例详解

    C++发邮件简单实例详解

    这篇文章主要为大家详细介绍了C++发邮件的简单实例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-05-05
  • OpenCV利用高斯模糊实现简单的磨皮美颜效果

    OpenCV利用高斯模糊实现简单的磨皮美颜效果

    这篇文章主要介绍了通过OpenCV中的高斯模糊以及双边模糊来实现一个简单的磨皮美颜效果,文中的讲解很详细,感兴趣的同学可以学习一下
    2021-12-12
  • C++中inet_pton、inet_ntop函数的用法

    C++中inet_pton、inet_ntop函数的用法

    这篇文章主要介绍了C++中inet_pton、inet_ntop函数的用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-08-08
  • 关于C++中由于字节对齐引起内存问题定位分析

    关于C++中由于字节对齐引起内存问题定位分析

    前几天遇到一个稀奇古怪的问题,在创建对象的时候程序异常退出,查找代码发现结构体数组问题,最终把问题简化得到解决方法,下面小编把我的问题及解决方案分享到脚本之家平台供大家参考下
    2021-06-06
  • C语言接口与实现方法实例详解

    C语言接口与实现方法实例详解

    这篇文章主要介绍了C语言接口与实现方法,包括接口的概念、实现方法及抽象数据类型等,并配合实例予以说明,需要的朋友可以参考下
    2014-09-09
  • C语言用数组实现反弹球消砖块

    C语言用数组实现反弹球消砖块

    这篇文章主要为大家详细介绍了C语言用数组实现反弹球消砖块,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-05-05
  • C语言近万字为你讲透树与二叉树

    C语言近万字为你讲透树与二叉树

    树是计算机算法最重要的非线性结构。因为树能很好地描述结构的分支关系和层次特性,所以在计算机科学和计算机应用领域有着广泛的应用。这篇文章我就带大家一起了解一下树、二叉树这种结构,下篇文章会重点向大家介绍二叉树的遍历算法
    2022-05-05
  • C++ OpenCV实现像素画的示例代码

    C++ OpenCV实现像素画的示例代码

    这篇文章主要介绍了通过OpenCV进行图片像素的变化,从而形成像素画效果的功能。文中的示例代码讲解详细,感兴趣的小伙伴可以动手试一试
    2022-01-01

最新评论