MST

星途 面试题库

面试题:C++中std::move在继承体系下的边界处理

构建一个继承体系,基类Base有虚析构函数,派生类Derived有一些资源需要管理。在Derived对象之间使用std::move进行转移时,会涉及到哪些边界情况?请编写代码详细展示并分析这些情况,包括内存管理、虚函数调用等方面。
13.6万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

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

class Base {
public:
    virtual ~Base() {
        std::cout << "Base destructor" << std::endl;
    }
};

class Derived : public Base {
private:
    int* data;
public:
    Derived() : data(new int(42)) {
        std::cout << "Derived constructor" << std::endl;
    }

    Derived(const Derived& other) : data(new int(*other.data)) {
        std::cout << "Derived copy constructor" << std::endl;
    }

    Derived(Derived&& other) noexcept : data(other.data) {
        other.data = nullptr;
        std::cout << "Derived move constructor" << std::endl;
    }

    Derived& operator=(const Derived& other) {
        if (this != &other) {
            delete data;
            data = new int(*other.data);
        }
        std::cout << "Derived copy assignment operator" << std::endl;
        return *this;
    }

    Derived& operator=(Derived&& other) noexcept {
        if (this != &other) {
            delete data;
            data = other.data;
            other.data = nullptr;
        }
        std::cout << "Derived move assignment operator" << std::endl;
        return *this;
    }

    ~Derived() {
        delete data;
        std::cout << "Derived destructor" << std::endl;
    }

    virtual void print() const {
        if (data) {
            std::cout << "Data in Derived: " << *data << std::endl;
        } else {
            std::cout << "Data in Derived is nullptr" << std::endl;
        }
    }
};

int main() {
    Derived d1;
    Derived d2 = std::move(d1);

    d2.print();
    d1.print();

    std::unique_ptr<Base> basePtr1 = std::make_unique<Derived>();
    std::unique_ptr<Base> basePtr2 = std::move(basePtr1);

    if (basePtr1) {
        basePtr1->print();
    } else {
        std::cout << "basePtr1 is nullptr after move" << std::endl;
    }

    if (basePtr2) {
        basePtr2->print();
    }

    return 0;
}

内存管理

  1. 移动构造函数:在Derived(Derived&& other) noexcept中,将otherdata指针直接赋值给当前对象,并将other.data置为nullptr,这样就避免了重复分配内存,实现了资源的转移。
  2. 移动赋值运算符:在Derived& operator=(Derived&& other) noexcept中,先释放当前对象的资源(delete data),然后将otherdata指针赋值过来,并将other.data置为nullptr

虚函数调用

  1. 通过基类指针:当使用std::unique_ptr<Base>来管理Derived对象,并进行std::move操作时,虚函数调用仍然能够正确执行。如代码中basePtr2->print()会调用Derived类的print函数,因为print是虚函数,动态绑定机制会根据对象的实际类型来调用正确的函数。
  2. 对象状态:移动操作后,源对象(如d1)处于有效但可能无意义的状态,这里d1.datanullptr,其print函数输出相应提示。而目标对象(如d2)获得了源对象的资源,其print函数能正常输出数据。