MST

星途 面试题库

面试题:C++ 右值引用中移动语义的实现原理

请阐述在C++中右值引用是如何实现移动语义的,并举一个简单的示例代码说明,比如一个自定义类的移动构造函数和移动赋值运算符重载的实现。
16.5万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

右值引用实现移动语义原理

在C++中,右值引用(&&)用于标识临时对象或即将销毁的对象。移动语义利用右值引用,允许我们将资源(如动态分配的内存)从一个对象高效地转移到另一个对象,而不是进行深拷贝。这样可以避免不必要的内存分配和释放操作,提高程序性能。

当一个对象作为右值传递给函数或构造函数时,我们可以通过右值引用参数来接收它,并在内部将其资源“移动”到新对象,同时将原对象置为一个可析构的状态(通常是将其内部指针设为 nullptr 等)。

示例代码

#include <iostream>
#include <memory>

class MyClass {
private:
    std::unique_ptr<int[]> data;
    size_t size;

public:
    // 构造函数
    MyClass(size_t s) : size(s), data(std::make_unique<int[]>(s)) {
        std::cout << "Constructor: Allocating memory for " << size << " elements." << std::endl;
    }

    // 析构函数
    ~MyClass() {
        std::cout << "Destructor: Deallocating memory." << std::endl;
    }

    // 拷贝构造函数
    MyClass(const MyClass& other) : size(other.size), data(std::make_unique<int[]>(other.size)) {
        std::cout << "Copy Constructor: Copying data." << std::endl;
        for (size_t i = 0; i < size; ++i) {
            data[i] = other.data[i];
        }
    }

    // 拷贝赋值运算符重载
    MyClass& operator=(const MyClass& other) {
        if (this != &other) {
            size = other.size;
            data = std::make_unique<int[]>(size);
            std::cout << "Copy Assignment: Copying data." << std::endl;
            for (size_t i = 0; i < size; ++i) {
                data[i] = other.data[i];
            }
        }
        return *this;
    }

    // 移动构造函数
    MyClass(MyClass&& other) noexcept : size(other.size), data(std::move(other.data)) {
        other.size = 0;
        other.data = nullptr;
        std::cout << "Move Constructor: Moving data." << std::endl;
    }

    // 移动赋值运算符重载
    MyClass& operator=(MyClass&& other) noexcept {
        if (this != &other) {
            size = other.size;
            data = std::move(other.data);
            other.size = 0;
            other.data = nullptr;
            std::cout << "Move Assignment: Moving data." << std::endl;
        }
        return *this;
    }
};

int main() {
    MyClass a(5);
    MyClass b = std::move(a); // 调用移动构造函数
    MyClass c(10);
    c = std::move(b); // 调用移动赋值运算符重载
    return 0;
}

在上述代码中:

  1. 移动构造函数MyClass(MyClass&& other) noexcept 接收一个右值引用参数 other,将 other 的资源(datasize)移动到当前对象,同时将 other 置为可析构的状态。
  2. 移动赋值运算符重载MyClass& operator=(MyClass&& other) noexcept 同样接收一个右值引用参数 other,先检查是否为自赋值,然后将 other 的资源移动到当前对象,并将 other 置为可析构的状态。

通过 std::move 函数可以将左值转换为右值,从而触发移动语义。在 main 函数中,通过 std::move 调用了移动构造函数和移动赋值运算符重载。