MST

星途 面试题库

面试题:C++中std::move在对象有自定义析构函数时的边界情况

假设有一个类MyClass,它有一个自定义的析构函数用于释放动态分配的内存。请编写代码展示在使用std::move转移对象所有权后,自定义析构函数是如何正确工作的,并解释其中的原理。
18.5万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试
#include <iostream>
#include <memory>

class MyClass {
private:
    int* data;
public:
    MyClass() : data(new int(42)) {
        std::cout << "Constructor: Allocated memory at " << data << std::endl;
    }

    ~MyClass() {
        if (data) {
            std::cout << "Destructor: Deallocating memory at " << data << std::endl;
            delete data;
        }
    }

    // 移动构造函数
    MyClass(MyClass&& other) noexcept : data(other.data) {
        other.data = nullptr;
        std::cout << "Move Constructor: Transferred ownership of memory at " << data << std::endl;
    }

    // 移动赋值运算符
    MyClass& operator=(MyClass&& other) noexcept {
        if (this != &other) {
            delete data;
            data = other.data;
            other.data = nullptr;
            std::cout << "Move Assignment: Transferred ownership of memory at " << data << std::endl;
        }
        return *this;
    }
};

int main() {
    MyClass obj1;
    MyClass obj2 = std::move(obj1);

    return 0;
}

原理解释

  1. 移动构造函数:当使用std::move转移对象所有权时,移动构造函数会被调用。在上述代码中,MyClass(MyClass&& other) noexceptother对象中的data指针直接赋值给新对象,然后将other.data设为nullptr。这样新对象就拥有了原对象动态分配的内存,而原对象不再拥有这块内存,避免了重复释放。
  2. 移动赋值运算符:如果是使用移动赋值(obj2 = std::move(obj1)),移动赋值运算符MyClass& operator=(MyClass&& other) noexcept会被调用。它先释放自身已有的内存(如果有),然后将other对象的data指针赋值过来,并将other.data设为nullptr
  3. 析构函数:在对象生命周期结束时,析构函数会被调用。由于移动操作后,只有新对象拥有动态分配内存的指针,所以只有新对象的析构函数会释放这块内存,而原对象(已被移动)的析构函数不会对已转移所有权的内存进行重复释放,因为其指针已被设为nullptr。这确保了动态分配内存的正确释放,避免了内存泄漏和悬空指针问题。