如何在同一容器中存储shared_ptr与weak_ptr?求优雅实现方案
优雅实现混合拥有/借用指针的容器
嘿,这个需求我之前也碰到过,你的holder思路方向是对的,但确实可以用更现代、更简洁的方式来实现。我给你分享两个比较优雅的方案,都是基于标准库或者轻量封装的:
方案一:用std::variant统一存储两种指针(C++17+)
C++17引入的std::variant天生适合这种“二选一”的存储场景,不用自定义结构体,直接用标准库组件就能搞定:
#include <variant> #include <memory> #include <vector> // 定义别名,让代码更清晰 template <typename T> using OwningOrBorrowingPtr = std::variant<std::shared_ptr<T>, std::weak_ptr<T>>; // 通用的获取可用shared_ptr的函数 template <typename T> std::shared_ptr<T> get_shared_ptr(const OwningOrBorrowingPtr<T>& ptr_var) { return std::visit([](auto&& ptr) -> std::shared_ptr<T> { using PtrType = std::decay_t<decltype(ptr)>; // 编译期判断是shared_ptr还是weak_ptr if constexpr (std::is_same_v<PtrType, std::shared_ptr<T>>) { return ptr; } else { return ptr.lock(); // weak_ptr转shared_ptr } }, ptr_var); } // 使用示例 struct MyData { void some_method() {} }; int main() { std::vector<OwningOrBorrowingPtr<MyData>> ptr_container; // 添加拥有的对象(容器持有shared_ptr) ptr_container.emplace_back(std::make_shared<MyData>()); // 添加借用的对象(容器持有weak_ptr,不拥有所有权) auto external_obj = std::make_shared<MyData>(); ptr_container.emplace_back(std::weak_ptr<MyData>(external_obj)); // 获取并使用指针 for (const auto& ptr_var : ptr_container) { if (auto ptr = get_shared_ptr(ptr_var)) { ptr->some_method(); } } }
这个方案的优势:
- 完全基于标准库,不用自定义复杂的结构体和运算符重载
std::visit是编译期分发,性能和直接调用一样高效- 类型安全,编译器会帮你检查只能存储
shared_ptr或weak_ptr
方案二:轻量封装的智能指针包装类
如果你需要更“透明”的用法(比如直接像普通智能指针一样解引用),可以写一个轻量的包装类,通过构造函数自动区分拥有/借用的情况:
#include <memory> #include <vector> template <typename T> class SmartPtrWrapper { private: std::shared_ptr<T> owned_ptr_; std::weak_ptr<T> borrowed_ptr_; public: // 从shared_ptr构造:容器拥有对象所有权 SmartPtrWrapper(std::shared_ptr<T> ptr) : owned_ptr_(std::move(ptr)) {} // 从weak_ptr构造:容器仅借用,不拥有所有权 SmartPtrWrapper(std::weak_ptr<T> ptr) : borrowed_ptr_(std::move(ptr)) {} // 获取可用的shared_ptr std::shared_ptr<T> get() const { return owned_ptr_ ? owned_ptr_ : borrowed_ptr_.lock(); } // 重载解引用运算符,用法和普通智能指针一致 T& operator*() const { return *get(); } // 重载箭头运算符 T* operator->() const { return get().get(); } // 隐式转换为bool,方便判断指针是否有效 explicit operator bool() const { return static_cast<bool>(get()); } }; // 使用示例 struct MyData { void some_method() {} }; int main() { std::vector<SmartPtrWrapper<MyData>> ptr_container; // 添加拥有的对象 ptr_container.emplace_back(std::make_shared<MyData>()); // 添加借用的对象 auto external_obj = std::make_shared<MyData>(); ptr_container.emplace_back(std::weak_ptr<MyData>(external_obj)); // 直接像普通智能指针一样使用 if (ptr_container[0]) { ptr_container[0]->some_method(); } }
这个方案的优势:
- 用法非常接近原生智能指针,学习成本低
- 封装了所有细节,调用方不需要关心是拥有还是借用
- 保持了类型安全,避免错误的指针类型赋值
一些注意事项
- 无论用哪种方案,都要注意
weak_ptr.lock()可能返回空指针(如果原对象已经被销毁),所以使用前一定要检查指针有效性 - 如果你的项目还在使用C++17之前的标准,可以用
boost::variant替代std::variant,用法基本一致 - 包装类方案可以根据需要补充拷贝/移动构造函数,但默认的行为已经符合预期:拷贝
owned_ptr_会增加引用计数,拷贝borrowed_ptr_只是拷贝弱引用
内容的提问来源于stack exchange,提问作者yesint
相关产品推荐
相关产品推荐

