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

如何在C++中实现自定义SharedPtr的类型转换功能

自定义Foo子类SharedPtr的转换实现方案

问题背景

我正在C++中实现一个仅支持Foo及其子类的自定义SharedPtr,用于学习智能指针的工作原理。当前类结构如下:

template <typename T, typename = std::enable_if_t<std::is_same_v<T, Foo> || std::is_base_of_v<Foo, T>>>
class ReferenceCounter final
{
private:
    T* m_Value;
    unsigned int m_Counter;
};


template <typename T, typename = std::enable_if_t<std::is_same_v<T, Foo> || std::is_base_of_v<Foo, T>>>
class SharedPtr final
{
private:
    ReferenceCounter<T>* m_ReferenceCounter;
};

已经完成了构造、拷贝、赋值等基础逻辑,现在需要实现两个核心转换功能:

  1. 子类SharedPtr隐式转换为基类,比如SharedPtr<Foo> f = MakeShared<FooDerived>();
  2. 基类SharedPtr安全向下转换为派生类,失败时返回空指针,比如SharedPtr<FooDerived> fd = TryCast<FooDerived>(f);

实现方案

1. 子类到基类的隐式转换

要支持这种隐式转换,关键是给SharedPtr添加模板构造函数,让基类实例能接收派生类的SharedPtr,同时先调整ReferenceCounter的设计,消除具体类型的束缚:

第一步:修改ReferenceCounter

将存储的指针改为void*,同时保存对应类型的析构函数,让不同类型的SharedPtr能复用同一个计数器:

template <typename T, typename = std::enable_if_t<std::is_same_v<T, Foo> || std::is_base_of_v<Foo, T>>>
class ReferenceCounter final
{
private:
    void* m_Value;
    unsigned int m_Counter;
    void (*m_Deleter)(void*); // 存储对应类型的析构逻辑

public:
    explicit ReferenceCounter(T* ptr) 
        : m_Value(ptr), m_Counter(1)
    {
        m_Deleter = [](void* p) { delete static_cast<T*>(p); };
    }

    void addRef() noexcept { ++m_Counter; }
    bool release() noexcept { return --m_Counter == 0; }
    void destroy() noexcept { m_Deleter(m_Value); }
    void* getRaw() const noexcept { return m_Value; }
};

第二步:给SharedPtr添加模板构造函数

在SharedPtr中添加仅允许派生类转换的模板构造函数,共享计数器并维护引用计数:

template <typename T, typename>
class SharedPtr final
{
private:
    ReferenceCounter<T>* m_ReferenceCounter;

public:
    // 其他已实现的构造、拷贝、赋值逻辑...

    // 子类到基类的隐式转换构造函数
    template <typename Derived, typename = std::enable_if_t<std::is_base_of_v<T, Derived>>>
    SharedPtr(const SharedPtr<Derived>& other) noexcept
    {
        if (other.m_ReferenceCounter)
        {
            m_ReferenceCounter = reinterpret_cast<ReferenceCounter<T>*>(other.m_ReferenceCounter);
            m_ReferenceCounter->addRef();
        }
        else
        {
            m_ReferenceCounter = nullptr;
        }
    }

    // 获取原始指针的方法
    T* get() const noexcept 
    {
        return m_ReferenceCounter ? static_cast<T*>(m_ReferenceCounter->getRaw()) : nullptr;
    }
};

这里的reinterpret_cast是安全的:所有ReferenceCounter的内存布局一致,且enable_if已确保Derived是T的子类,指针转换合法。


2. 基类到派生类的安全转换(TryCast)

通过全局模板函数结合dynamic_cast做运行时类型检查,实现安全向下转换:

第一步:声明友元函数

让TryCast能访问SharedPtr的私有成员,在SharedPtr中提前声明友元:

// 提前声明TryCast和SharedPtr
template <typename Derived, typename Base>
std::enable_if_t<std::is_base_of_v<Foo, Derived> && std::is_base_of_v<Foo, Base>, SharedPtr<Derived>>
TryCast(const SharedPtr<Base>& basePtr) noexcept;

template <typename T, typename>
class SharedPtr final
{
private:
    ReferenceCounter<T>* m_ReferenceCounter;

    // 声明TryCast为友元
    template <typename Derived, typename Base>
    friend std::enable_if_t<std::is_base_of_v<Foo, Derived> && std::is_base_of_v<Foo, Base>, SharedPtr<Derived>>
    TryCast(const SharedPtr<Base>& basePtr) noexcept;

    // 其他成员...
};

第二步:实现TryCast函数

template <typename Derived, typename Base>
std::enable_if_t<std::is_base_of_v<Foo, Derived> && std::is_base_of_v<Foo, Base>, SharedPtr<Derived>>
TryCast(const SharedPtr<Base>& basePtr) noexcept
{
    SharedPtr<Derived> result;

    if (!basePtr.m_ReferenceCounter)
    {
        return result; // 基类指针为空,直接返回空
    }

    // 尝试动态转换基类指针到派生类
    Base* baseRaw = basePtr.get();
    Derived* derivedRaw = dynamic_cast<Derived*>(baseRaw);

    if (derivedRaw)
    {
        // 转换成功,共享计数器并增加引用
        result.m_ReferenceCounter = reinterpret_cast<ReferenceCounter<Derived>*>(basePtr.m_ReferenceCounter);
        result.m_ReferenceCounter->addRef();
    }

    return result;
}

如果dynamic_cast失败,返回的SharedPtr<Derived>会保持空状态;转换成功则共享原计数器,保证引用计数正确。


内容的提问来源于stack exchange,提问作者Bruno Jácome

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 13:29:56