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

如何在同一容器中存储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();
    }
}

这个方案的优势:

  • 用法非常接近原生智能指针,学习成本低
  • 封装了所有细节,调用方不需要关心是拥有还是借用
  • 保持了类型安全,避免错误的指针类型赋值

一些注意事项

  1. 无论用哪种方案,都要注意weak_ptr.lock()可能返回空指针(如果原对象已经被销毁),所以使用前一定要检查指针有效性
  2. 如果你的项目还在使用C++17之前的标准,可以用boost::variant替代std::variant,用法基本一致
  3. 包装类方案可以根据需要补充拷贝/移动构造函数,但默认的行为已经符合预期:拷贝owned_ptr_会增加引用计数,拷贝borrowed_ptr_只是拷贝弱引用

内容的提问来源于stack exchange,提问作者yesint

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 15:22:39