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

能否用C++标准库实现可转移唯一所有权的多副本智能指针?

实现需求的智能指针方案

核心需求拆解

你需要的智能指针要同时满足这些特性:

  • 像std::unique_ptr一样,拥有者出作用域时销毁资源
  • 支持多次复制,副本仅作为资源引用,同一时刻仅一个实例持有所有权
  • 可从std::weak_ptr构造,获取临时所有权,出作用域时强制销毁资源并让其他所有指针失效
  • 支持release()转移所有权,实现线程间托管切换

标准库直接实现?不行

C++标准库的智能指针没有直接匹配的:

  • std::unique_ptr独占所有权,无法复制
  • std::shared_ptr共享所有权,只有最后一个指针析构时才销毁资源
  • std::weak_ptr只是弱引用,无法获取独占所有权

必须基于标准库组件封装,或者自定义控制块实现。

优雅实现方案:自定义twin_ptr(基于控制块)

下面是一个可落地的实现思路,用自定义控制块管理资源、所有权状态和引用计数,结合标准库的原子操作保证线程安全:

核心设计思路

  1. 控制块(ControlBlock):作为资源的唯一管理者,包含:
    • std::unique_ptr<T>:存储真正的资源
    • std::atomic<bool>:标记当前是否有活跃所有者
    • std::atomic<int>:跟踪twin_ptr实例的引用计数
  2. twin_ptr类:封装控制块指针,复制时仅增加控制块的引用计数,不转移所有权
  3. 所有权规则:
    • 只有标记为“所有者”的twin_ptr析构时,才会销毁资源,并将控制块内的资源置空、所有权标记设为false
    • 所有副本访问资源前,都会检查控制块的有效性和所有权状态
  4. release()方法:将当前实例的所有权标记转移给目标实例,实现托管切换

简化代码示例

#include <memory>
#include <atomic>
#include <stdexcept>

template<typename T>
class twin_ptr {
private:
    struct ControlBlock {
        std::unique_ptr<T> resource;
        std::atomic<bool> is_active_owner{false};
        std::atomic<int> ref_count{0};

        explicit ControlBlock(std::unique_ptr<T> res) 
            : resource(std::move(res)) {
            is_active_owner = true;
            ref_count = 1;
        }

        void add_ref() { ref_count.fetch_add(1, std::memory_order_relaxed); }
        bool release_ref() { 
            return ref_count.fetch_sub(1, std::memory_order_acq_rel) == 1; 
        }
    };

    ControlBlock* cb = nullptr;

    // 私有构造:用于复制场景
    explicit twin_ptr(ControlBlock* block) : cb(block) {
        if (cb) cb->add_ref();
    }

public:
    // 从unique_ptr构造,获取初始所有权
    explicit twin_ptr(std::unique_ptr<T> res) {
        if (res) cb = new ControlBlock(std::move(res));
    }

    // 从weak_ptr构造:先lock获取shared_ptr,再转移所有权
    // 注意:此操作会让原shared_ptr失效,因为资源被转移到twin_ptr的控制块
    explicit twin_ptr(std::weak_ptr<T> wp) {
        auto sp = wp.lock();
        if (sp) {
            cb = new ControlBlock(std::unique_ptr<T>(sp.release()));
        }
    }

    // 复制构造:共享控制块,不转移所有权
    twin_ptr(const twin_ptr& other) : cb(other.cb) {
        if (cb) cb->add_ref();
    }

    // 复制赋值
    twin_ptr& operator=(const twin_ptr& other) {
        if (this != &other) {
            cleanup();
            cb = other.cb;
            if (cb) cb->add_ref();
        }
        return *this;
    }

    // 移动构造
    twin_ptr(twin_ptr&& other) noexcept : cb(other.cb) {
        other.cb = nullptr;
    }

    // 移动赋值
    twin_ptr& operator=(twin_ptr&& other) noexcept {
        if (this != &other) {
            cleanup();
            cb = other.cb;
            other.cb = nullptr;
        }
        return *this;
    }

    // 析构函数
    ~twin_ptr() { cleanup(); }

    // 转移所有权到目标twin_ptr
    void release(twin_ptr& target) {
        if (!cb || !cb->is_active_owner || this == &target) return;

        // 确保目标先清理自身旧控制块
        target.cleanup();
        // 共享当前控制块
        target.cb = cb;
        cb->add_ref();
        // 转移所有权标记
        cb->is_active_owner.store(false, std::memory_order_release);
        target.cb->is_active_owner.store(true, std::memory_order_release);
    }

    // 资源访问
    T* get() const {
        if (!cb || !cb->is_active_owner || !cb->resource) return nullptr;
        return cb->resource.get();
    }

    T& operator*() const {
        auto ptr = get();
        if (!ptr) throw std::runtime_error("twin_ptr: invalid resource access");
        return *ptr;
    }

    T* operator->() const {
        auto ptr = get();
        if (!ptr) throw std::runtime_error("twin_ptr: invalid resource access");
        return ptr;
    }

    // 检查是否持有所有权
    bool has_ownership() const {
        return cb && cb->is_active_owner.load(std::memory_order_acquire);
    }

    // 检查资源是否有效(未被销毁)
    bool valid() const {
        return cb && cb->resource != nullptr;
    }

private:
    void cleanup() {
        if (!cb) return;

        bool is_last_ref = cb->release_ref();
        // 如果当前是所有者,销毁资源并标记失效
        if (cb->is_active_owner.load(std::memory_order_acquire)) {
            cb->resource.reset();
            cb->is_active_owner.store(false, std::memory_order_release);
        }
        // 最后一个引用销毁时,释放控制块
        if (is_last_ref) delete cb;
        cb = nullptr;
    }
};

关键特性说明

  • 作用域销毁:持有所有权的twin_ptr析构时,无论是否有其他副本存在,都会立即销毁资源
  • 可复制性:副本仅共享控制块,不拥有资源所有权,不会触发资源销毁
  • weak_ptr构造:通过lock获取shared_ptr后,将资源转移到控制块,实现临时所有权接管
  • 所有权转移:release()方法通过原子操作切换所有权标记,安全实现线程间托管切换
  • 线程安全:控制块内的状态用原子操作更新,避免多线程竞争问题

其他替代思路

如果不想完全自定义,也可以用std::shared_ptr封装一个“所有权代理”类,代理类内部用std::unique_ptr管理资源,配合原子标记跟踪所有者。但本质上和上面的控制块方案逻辑一致,只是实现形式不同。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 12:15:27