如何在C++中实现自定义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; };
已经完成了构造、拷贝、赋值等基础逻辑,现在需要实现两个核心转换功能:
- 子类
SharedPtr隐式转换为基类,比如SharedPtr<Foo> f = MakeShared<FooDerived>(); - 基类
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
相关产品推荐
相关产品推荐

