如何复制存储在基类智能指针中的派生类对象?
如何复制基类智能指针中的派生类对象?
问题本质:对象切片
你尝试的模板函数之所以只复制出Base类对象,核心原因是对象切片:模板的类型推导在编译期完成,当传入std::shared_ptr<Base>时,模板参数T会被推导为Base,此时std::make_shared<T>(*in)只会复制基类部分,派生类的成员和多态信息全部丢失,最终得到的是一个纯Base对象。
标准解决方案:虚克隆方法
虽然需要给每个类添加duplicate方法,但这是C++中解决多态对象复制的标准方案——只有类自身才清楚自己的真实类型,能完整复制所有成员。你给出的实现是正确的,核心逻辑如下:
- 在基类中定义虚克隆方法,返回基类智能指针
- 每个派生类重写该方法,返回自身类型的智能指针(利用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
相关产品推荐
相关产品推荐

