能否使shared_ptr实现协变?附代码示例求验证
好问题!你遇到的是C++协变返回类型规则和智能指针之间的小冲突——标准里的协变只支持指针和引用类型,而std::shared_ptr是个类模板实例,直接这么写确实过不了编译器。不过我们有几种办法来实现你想要的效果,让代码合法又满足需求:
方法1:指针协变+shared_ptr包装
先回到你原来能用的指针版本(这个是符合协变规则的),然后在调用的时候把返回的指针塞进shared_ptr里管理内存。这样既保留了多态,又用上了智能指针的自动内存管理:
#include <memory> struct BaseSPtr{}; struct DerivedSPtr : public BaseSPtr{}; class Base{ public: virtual BaseSPtr* function() = 0; virtual ~Base() = default; // 基类必须有虚析构,避免内存泄漏 }; class Derived : public Base { public: DerivedSPtr* function() override { return new DerivedSPtr(); } }; // 使用示例 int main() { std::unique_ptr<Base> obj = std::make_unique<Derived>(); // 转换为BaseSPtr的shared_ptr std::shared_ptr<BaseSPtr> sptr(obj->function()); // 如果需要DerivedSPtr的shared_ptr,直接static_cast(因为我们知道类型) std::shared_ptr<DerivedSPtr> dsptr(static_cast<DerivedSPtr*>(obj->function())); return 0; }
方法2:重写函数返回可转换的shared_ptr
如果不想改返回类型的本质,那可以让派生类的override函数返回基类要求的shared_ptr<BaseSPtr>,但内部返回shared_ptr<DerivedSPtr>——因为派生类的shared_ptr可以隐式转换成基类的。要是你需要直接拿到DerivedSPtr的智能指针,就用std::dynamic_pointer_cast转一下,或者给派生类加个专门的非虚函数直接返回:
#include <memory> struct BaseSPtr{}; struct DerivedSPtr : public BaseSPtr{}; class Base{ public: virtual std::shared_ptr<BaseSPtr> function() = 0; virtual ~Base() = default; }; class Derived : public Base { public: // 符合override规则的版本,返回基类要求的类型 std::shared_ptr<BaseSPtr> function() override { return std::make_shared<DerivedSPtr>(); } // 额外提供非虚函数,直接返回DerivedSPtr的shared_ptr std::shared_ptr<DerivedSPtr> functionDerived() { return std::make_shared<DerivedSPtr>(); } }; // 使用示例 int main() { std::unique_ptr<Base> baseObj = std::make_unique<Derived>(); auto baseSptr = baseObj->function(); // 安全转换为DerivedSPtr的shared_ptr auto derivedSptr = std::dynamic_pointer_cast<DerivedSPtr>(baseSptr); // 如果是Derived类型的指针,直接调用专门的函数 std::unique_ptr<Derived> derivedObj = std::make_unique<Derived>(); auto directDerivedSptr = derivedObj->functionDerived(); return 0; }
方法3:自定义支持协变的智能指针包装类
如果你非要让override的函数直接返回和派生类绑定的智能指针,那可以自定义一个包装类,让包装类之间形成继承关系,这样就能利用协变规则了。虽然有点绕,但确实能实现:
#include <memory> struct BaseSPtr{}; struct DerivedSPtr : public BaseSPtr{}; // 基类智能指针包装,提供统一的接口 class BaseSPtrWrapper { public: virtual ~BaseSPtrWrapper() = default; virtual std::shared_ptr<BaseSPtr> get() const = 0; }; // 派生类智能指针包装,持有DerivedSPtr的shared_ptr class DerivedSPtrWrapper : public BaseSPtrWrapper { private: std::shared_ptr<DerivedSPtr> ptr_; public: DerivedSPtrWrapper(std::shared_ptr<DerivedSPtr> ptr) : ptr_(std::move(ptr)) {} std::shared_ptr<BaseSPtr> get() const override { return ptr_; // 隐式转换为基类的shared_ptr } // 额外提供获取DerivedSPtr的接口 std::shared_ptr<DerivedSPtr> getDerived() const { return ptr_; } }; class Base{ public: virtual BaseSPtrWrapper* function() = 0; virtual ~Base() = default; }; class Derived : public Base { public: DerivedSPtrWrapper* function() override { return new DerivedSPtrWrapper(std::make_shared<DerivedSPtr>()); } }; // 使用示例 int main() { std::unique_ptr<Base> obj = std::make_unique<Derived>(); std::unique_ptr<BaseSPtrWrapper> wrapper(obj->function()); auto baseSptr = wrapper->get(); // 转换为派生类包装,获取DerivedSPtr的shared_ptr auto derivedWrapper = dynamic_cast<DerivedSPtrWrapper*>(wrapper.get()); if (derivedWrapper) { auto derivedSptr = derivedWrapper->getDerived(); } return 0; }
总结
标准C++不允许直接将std::shared_ptr<Derived>作为std::shared_ptr<Base>的协变返回类型,因为协变仅支持指针和引用类型。但通过上述方法,我们可以实现类似的效果,既满足多态需求,又能返回派生类的shared_ptr。
内容的提问来源于stack exchange,提问作者Ionut Alexandru
相关产品推荐
相关产品推荐

