MST

星途 面试题库

面试题:C++ 重写拷贝构造函数在复杂继承体系中的问题与解决

构建一个C++的继承体系,基类中有动态分配资源的成员变量。派生类在此基础上又增加了新的动态资源(如网络连接,假设用自定义的`NetworkConnection`类表示)。请重写基类和派生类的拷贝构造函数,确保在拷贝对象时整个继承体系中的资源都能正确复制且不出现内存泄漏或其他未定义行为。并阐述在这种复杂继承体系下重写拷贝构造函数需要重点关注的要点。
38.6万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

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

// 自定义的NetworkConnection类
class NetworkConnection {
public:
    NetworkConnection() { std::cout << "NetworkConnection constructor" << std::endl; }
    ~NetworkConnection() { std::cout << "NetworkConnection destructor" << std::endl; }
    NetworkConnection(const NetworkConnection& other) { std::cout << "NetworkConnection copy constructor" << std::endl; }
    NetworkConnection& operator=(const NetworkConnection& other) {
        std::cout << "NetworkConnection copy assignment operator" << std::endl;
        return *this;
    }
};

class Base {
private:
    int* data;

public:
    Base() : data(new int(0)) {
        std::cout << "Base constructor" << std::endl;
    }

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

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

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

class Derived : public Base {
private:
    NetworkConnection network;

public:
    Derived() {
        std::cout << "Derived constructor" << std::endl;
    }

    Derived(const Derived& other) : Base(other), network(other.network) {
        std::cout << "Derived copy constructor" << std::endl;
    }

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

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

在这种复杂继承体系下重写拷贝构造函数需要重点关注的要点:

  1. 调用基类拷贝构造函数:在派生类的拷贝构造函数中,首先要调用基类的拷贝构造函数,以确保基类部分的资源能够正确复制。这保证了继承体系中基类成员变量的状态和资源在拷贝过程中被正确处理。
  2. 处理派生类新增资源:除了基类的资源,派生类自己新增的动态资源(如NetworkConnection对象)也需要正确地进行复制。在拷贝构造函数和赋值运算符重载函数中,要为这些新增资源提供合适的复制逻辑,通常是调用它们自身的拷贝构造函数。
  3. 避免内存泄漏:在赋值运算符重载函数中,要先释放自身已有的资源,再进行新资源的复制。在整个继承体系中,确保每个对象在析构时正确释放其动态分配的资源,避免出现内存泄漏。特别是在派生类对象析构时,要保证基类和派生类的资源都能被正确释放,这依赖于合理的构造和析构函数设计。
  4. 遵循“复制即初始化”原则:拷贝构造函数应该完整地初始化对象的状态,就像通过正常构造函数创建一个新对象一样。这确保了对象在拷贝后处于一个合理且可用的状态,避免出现未定义行为。
  5. 一致性:在整个继承体系中,所有的拷贝构造函数和赋值运算符重载函数的行为应该保持一致。例如,如果在基类中采用了深拷贝,派生类也应该同样采用深拷贝,以维持对象语义的一致性。