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

如何复制存储在基类智能指针中的派生类对象?

如何复制基类智能指针中的派生类对象?

问题本质:对象切片

你尝试的模板函数之所以只复制出Base类对象,核心原因是对象切片:模板的类型推导在编译期完成,当传入std::shared_ptr<Base>时,模板参数T会被推导为Base,此时std::make_shared<T>(*in)只会复制基类部分,派生类的成员和多态信息全部丢失,最终得到的是一个纯Base对象。

标准解决方案:虚克隆方法

虽然需要给每个类添加duplicate方法,但这是C++中解决多态对象复制的标准方案——只有类自身才清楚自己的真实类型,能完整复制所有成员。你给出的实现是正确的,核心逻辑如下:

  1. 在基类中定义虚克隆方法,返回基类智能指针
  2. 每个派生类重写该方法,返回自身类型的智能指针(利用C++协变返回类型特性)

修改后的完整可运行代码:

#include <cstdlib>
#include <iostream>
#include <memory>

class Base
{
public:
    double a;
    Base(double a) : a(a) {}

    virtual void hello() const
    {
        std::cout << "Hello, I'm a base class.\n";
    }

    virtual std::shared_ptr<Base> duplicate() const
    {
        return std::make_shared<Base>(*this);
    }
};

class Derived : public Base
{
public:
    double b;
    Derived(double a, double b) : Base(a), b(b) {}

    virtual void hello() const override
    {
        std::cout << "Hello, I'm a derived class.\n";
    }

    virtual std::shared_ptr<Base> duplicate() const override
    {
        return std::make_shared<Derived>(*this);
    }
};

int main()
{
    std::shared_ptr<Base> base = std::make_shared<Base>(1.0);
    base->hello();

    std::shared_ptr<Base> derived = std::make_shared<Derived>(2.0, 3.0);
    derived->hello();

    std::shared_ptr<Base> duplicated = derived->duplicate();
    std::cout << "Duplicated: ";
    duplicated->hello(); // 输出Hello, I'm a derived class.

    return EXIT_SUCCESS;
}

简化重复代码:CRTP模板封装

如果不想为每个派生类手动编写duplicate方法,可以用**CRTP(奇异递归模板模式)**封装克隆逻辑,减少重复代码:

#include <cstdlib>
#include <iostream>
#include <memory>

template<typename Derived, typename Base>
class Cloneable : public Base
{
public:
    using Base::Base; // 继承基类构造函数

    std::shared_ptr<Base> duplicate() const override
    {
        return std::make_shared<Derived>(static_cast<const Derived&>(*this));
    }
};

// 基类定义
class Base
{
public:
    double a;
    Base(double a) : a(a) {}

    virtual void hello() const
    {
        std::cout << "Hello, I'm a base class.\n";
    }

    virtual std::shared_ptr<Base> duplicate() const = 0;
};

// 派生类只需继承Cloneable模板
class Derived : public Cloneable<Derived, Base>
{
public:
    double b;
    Derived(double a, double b) : Cloneable(a), b(b) {}

    virtual void hello() const override
    {
        std::cout << "Hello, I'm a derived class.\n";
    }
};

int main()
{
    std::shared_ptr<Base> derived = std::make_shared<Derived>(2.0, 3.0);
    std::shared_ptr<Base> duplicated = derived->duplicate();
    duplicated->hello(); // 正确输出派生类信息
    return EXIT_SUCCESS;
}

为什么没有“单一复制函数”的方案?

C++中不存在完全不需要修改类的单一函数能解决这个问题:运行期类型信息(RTTI)虽然能获取对象真实类型,但用dynamic_cast或typeid硬编码复制逻辑会导致代码耦合度极高,新增派生类时必须修改复制函数,维护成本远高于虚克隆方法。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.02 06:57:26