You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基类指针为私有成员时实现多态深拷贝的C++技术问题

实现多态对象的深拷贝(避免派生类类型丢失)

你的问题核心是多态场景下的深拷贝切片问题:当前代码里new A(*a)会触发对象切片,只拷贝基类A的部分,派生类B的成员和类型信息都丢失了,所以c2、c3调用的是A的print方法。

解决这个问题的标准方案是给基类添加虚克隆(clone)函数,让每个派生类实现自己的克隆逻辑,这样就能在运行时根据实际对象类型完成正确的深拷贝,而且扩展性远优于枚举方案。

具体修改步骤:

  1. 给基类A添加虚clone方法
    把clone声明为虚函数,若A允许实例化,就提供默认实现;如果A是抽象类,就设为纯虚函数。这里我们让A可实例化,所以:

    virtual A* clone() const {
        return new A(*this);
    }
    
  2. 派生类B重写clone方法
    利用C++的协变返回类型特性,返回B*更直观:

    B* clone() const override {
        return new B(*this);
    }
    
  3. 修改类C的构造逻辑
    把原来直接new A(*a)的代码,替换为调用对象的clone方法:

    • 参数化构造函数:
      C(A* a) {
          this->a = a->clone();
      }
      
    • 拷贝构造函数:
      C(const C& copy) {
          a = nullptr;
          if (copy.a != nullptr) {
              a = copy.a->clone();
          }
      }
      

修改后的完整代码:

#include <iostream>

class A
{
protected:
    double a;
    double b;

public:
    A() : a(0.0), b(0.0) {}
    A(double a, double b) : a(a), b(b) {}
    A(const A& copy) : a(copy.a), b(copy.b) {}

    // 添加虚clone方法
    virtual A* clone() const {
        return new A(*this);
    }

    virtual void print()
    {
        std::cout << "print A" << std::endl;
        std::cout << "a: " << a << ", b: " << b << std::endl;
    }

    // 基类要加虚析构函数!避免内存泄漏
    virtual ~A() = default;
};

class B : public A
{
private:
    double c;

public:
    B() : A(), c(0.0) {}
    B(double a, double b, double c) : A(a, b), c(c) {}
    B(const B& copy) : A(copy), c(copy.c) {}

    // 重写clone方法
    B* clone() const override {
        return new B(*this);
    }

    virtual void print()
    {
        std::cout << "print B" << std::endl;
        std::cout << "a: " << a << ", b: " << b << ", c: " << c << std::endl;
    }
};

class C
{
private:
    A* a;

public:
    C() : a(nullptr) {}

    // 修改为调用clone
    C(A* a) {
        this->a = a->clone();
    }

    // 拷贝构造也调用clone
    C(const C& copy) {
        a = nullptr;
        if (copy.a != nullptr) {
            a = copy.a->clone();
        }
    }

    // 建议添加赋值运算符重载,遵循三/五法则
    C& operator=(const C& copy) {
        if (this != &copy) {
            delete a;
            a = copy.a ? copy.a->clone() : nullptr;
        }
        return *this;
    }

    ~C() {
        delete a;
        a = nullptr;
    }

    void print()
    {
        if (a != nullptr)
            a->print();
    }
};

int main()
{
    A* a = new A(3.5, 4.5);
    A* b = new B(3.5, 4.5, 5.5);
    C c1(a), c2(b), c3(c2);
    delete a;
    delete b;

    c1.print();
    c2.print();
    c3.print();
}

关键细节说明:

  • 虚析构函数:基类A必须添加虚析构函数,否则delete指向派生类的基类指针时,只会调用基类析构函数,导致派生类成员内存泄漏。
  • 三/五法则:类C拥有裸指针成员,建议补充赋值运算符重载,确保拷贝赋值时也能正确深拷贝,避免浅拷贝导致的重复释放问题。
  • 扩展性:新增派生类时,只需要在新类里重写clone方法即可,完全不用修改基类A和类C的代码,符合开闭原则,比枚举方案灵活得多。

运行修改后的代码,c2和c3会正确调用B的print方法,输出派生类的内容。

内容的提问来源于stack exchange,提问作者Nathan Sandvig

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 23:10:43