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

能否使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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 17:17:52