MST

星途 面试题库

面试题:C++ 中自定义类型的拷贝赋值运算符重载与移动赋值运算符重载的区别与实现

假设你有一个包含动态分配内存成员的自定义 C++ 类型,如自定义的字符串类。请分别实现该类的拷贝赋值运算符重载和移动赋值运算符重载,并解释两者在性能和资源管理上的主要区别,同时说明在现代 C++ 编程中移动语义的重要性。
37.6万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

自定义字符串类示例代码

#include <iostream>
#include <cstring>

class MyString {
private:
    char* data;
    size_t length;

public:
    // 构造函数
    MyString(const char* str = nullptr) {
        if (str == nullptr) {
            length = 0;
            data = new char[1];
            data[0] = '\0';
        } else {
            length = std::strlen(str);
            data = new char[length + 1];
            std::strcpy(data, str);
        }
    }

    // 析构函数
    ~MyString() {
        delete[] data;
    }

    // 拷贝构造函数
    MyString(const MyString& other) {
        length = other.length;
        data = new char[length + 1];
        std::strcpy(data, other.data);
    }

    // 拷贝赋值运算符重载
    MyString& operator=(const MyString& other) {
        if (this == &other) {
            return *this;
        }
        delete[] data;
        length = other.length;
        data = new char[length + 1];
        std::strcpy(data, other.data);
        return *this;
    }

    // 移动构造函数
    MyString(MyString&& other) noexcept {
        data = other.data;
        length = other.length;
        other.data = nullptr;
        other.length = 0;
    }

    // 移动赋值运算符重载
    MyString& operator=(MyString&& other) noexcept {
        if (this == &other) {
            return *this;
        }
        delete[] data;
        data = other.data;
        length = other.length;
        other.data = nullptr;
        other.length = 0;
        return *this;
    }

    void print() const {
        std::cout << data << std::endl;
    }
};

拷贝赋值运算符重载与移动赋值运算符重载的区别

  1. 性能区别
    • 拷贝赋值运算符重载:会进行深拷贝,即分配新的内存并将源对象的数据逐字节复制到新内存中。这涉及内存分配和数据复制操作,性能开销较大,尤其是当对象包含大量数据时。
    • 移动赋值运算符重载:执行的是浅拷贝,只是将源对象的资源(如指针)转移到目标对象,然后将源对象的资源指针设为 nullptr。没有新的内存分配和数据复制,性能更高。
  2. 资源管理区别
    • 拷贝赋值运算符重载:源对象和目标对象各自拥有独立的资源副本,修改其中一个对象不会影响另一个。
    • 移动赋值运算符重载:源对象的资源被转移给目标对象,源对象处于一个有效但可能无意义的状态(如指针为 nullptr)。源对象的析构函数不会释放已转移的资源,避免了重复释放内存的问题。

移动语义在现代C++编程中的重要性

  1. 提高性能:在对象传递和赋值过程中,避免不必要的数据拷贝,特别是对于大对象或动态分配内存的对象,显著提升程序性能。
  2. 优化容器操作:标准库容器(如 std::vectorstd::list 等)在插入、删除或重新分配元素时,利用移动语义提高效率。例如,当 std::vector 需要重新分配内存时,会使用移动操作将旧元素转移到新内存位置,而不是拷贝。
  3. 支持右值引用:移动语义是右值引用的重要应用,使C++ 能够更好地区分左值和右值,在函数重载和模板匹配时提供更灵活高效的选择,进一步优化代码性能。