**如何在C++中实现自定义智能指针并支持共享与弱引用?**

在现代C++中,智能指针是管理动态资源的核心工具,标准库提供了std::unique_ptrstd::shared_ptrstd::weak_ptr等实现。然而,实际项目中常常需要根据业务场景自定义智能指针,例如添加额外的引用计数策略、内存池管理或线程安全控制。下面我们将从头实现一个简易的自定义智能指针,支持共享引用计数和弱引用。

1. 设计目标

  • 共享引用计数:多对象共享同一资源,资源在最后一个强引用销毁时释放。
  • 弱引用:不影响引用计数,只能检查资源是否仍存在,且可在资源已释放后自动置空。
  • 线程安全:使用原子操作保证计数器在多线程环境下安全更新。
  • 可自定义分配器:通过模板参数指定资源分配策略。

2. 基础结构

#include <atomic>
#include <memory>
#include <iostream>
#include <type_traits>

template<typename T, typename Deleter = std::default_delete<T>>
class SharedPtr;

template<typename T>
class WeakPtr;

3. 共享控制块

控制块存放引用计数、弱计数以及删除器。

template<typename T, typename Deleter>
class ControlBlock {
public:
    explicit ControlBlock(T* ptr, Deleter del)
        : ptr_(ptr), deleter_(std::move(del)), strong_(1), weak_(0) {}

    void add_strong() noexcept { strong_.fetch_add(1, std::memory_order_relaxed); }
    void release_strong() noexcept {
        if (strong_.fetch_sub(1, std::memory_order_acq_rel) == 1) {
            deleter_(ptr_);
            release_weak(); // weak count includes the shared pointer itself
        }
    }

    void add_weak() noexcept { weak_.fetch_add(1, std::memory_order_relaxed); }
    void release_weak() noexcept {
        if (weak_.fetch_sub(1, std::memory_order_acq_rel) == 1) {
            delete this;
        }
    }

    T* get() const noexcept { return ptr_; }

private:
    T* ptr_;
    Deleter deleter_;
    std::atomic <size_t> strong_;
    std::atomic <size_t> weak_;
};

4. SharedPtr 实现

template<typename T, typename Deleter>
class SharedPtr {
public:
    using element_type = T;

    // 默认构造为空指针
    constexpr SharedPtr() noexcept : cb_(nullptr) {}

    explicit SharedPtr(T* ptr, Deleter del = Deleter{}) {
        cb_ = new ControlBlock<T, Deleter>(ptr, std::move(del));
    }

    // 拷贝构造
    SharedPtr(const SharedPtr& other) noexcept : cb_(other.cb_) {
        if (cb_) cb_->add_strong();
    }

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

    // 析构
    ~SharedPtr() { release(); }

    // 拷贝赋值
    SharedPtr& operator=(const SharedPtr& other) noexcept {
        if (this != &other) {
            release();
            cb_ = other.cb_;
            if (cb_) cb_->add_strong();
        }
        return *this;
    }

    // 移动赋值
    SharedPtr& operator=(SharedPtr&& other) noexcept {
        if (this != &other) {
            release();
            cb_ = other.cb_;
            other.cb_ = nullptr;
        }
        return *this;
    }

    // 访问操作符
    T& operator*() const noexcept { return *cb_->get(); }
    T* operator->() const noexcept { return cb_->get(); }

    T* get() const noexcept { return cb_ ? cb_->get() : nullptr; }

    size_t use_count() const noexcept { return cb_ ? cb_->strong_.load(std::memory_order_relaxed) : 0; }

    explicit operator bool() const noexcept { return get() != nullptr; }

    // 生成弱指针
    WeakPtr <T> weak_from_this() const noexcept {
        return WeakPtr <T>(*this);
    }

private:
    ControlBlock<T, Deleter>* cb_;

    void release() noexcept {
        if (cb_) cb_->release_strong();
    }

    friend class WeakPtr <T>;
};

5. WeakPtr 实现

template<typename T>
class WeakPtr {
public:
    constexpr WeakPtr() noexcept : cb_(nullptr) {}

    // 从 SharedPtr 构造
    WeakPtr(const SharedPtr<T, std::default_delete<T>>& sp) noexcept : cb_(sp.cb_) {
        if (cb_) cb_->add_weak();
    }

    // 拷贝构造
    WeakPtr(const WeakPtr& other) noexcept : cb_(other.cb_) {
        if (cb_) cb_->add_weak();
    }

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

    // 析构
    ~WeakPtr() { release(); }

    // 拷贝赋值
    WeakPtr& operator=(const WeakPtr& other) noexcept {
        if (this != &other) {
            release();
            cb_ = other.cb_;
            if (cb_) cb_->add_weak();
        }
        return *this;
    }

    // 移动赋值
    WeakPtr& operator=(WeakPtr&& other) noexcept {
        if (this != &other) {
            release();
            cb_ = other.cb_;
            other.cb_ = nullptr;
        }
        return *this;
    }

    // 尝试生成 SharedPtr
    SharedPtr<T, std::default_delete<T>> lock() const noexcept {
        if (cb_ && cb_->strong_.load(std::memory_order_acquire) > 0) {
            // 先增加 strong 计数,再返回 SharedPtr
            cb_->add_strong();
            return SharedPtr<T, std::default_delete<T>>(cb_);
        }
        return SharedPtr<T, std::default_delete<T>>(); // 空指针
    }

    bool expired() const noexcept {
        return !cb_ || cb_->strong_.load(std::memory_order_acquire) == 0;
    }

    size_t use_count() const noexcept {
        return cb_ ? cb_->strong_.load(std::memory_order_relaxed) : 0;
    }

private:
    // 用于内部直接构造 SharedPtr
    template<typename, typename>
    friend class SharedPtr;

    ControlBlock<T, std::default_delete<T>>* cb_;

    void release() noexcept {
        if (cb_) cb_->release_weak();
    }
};

6. 简单测试

int main() {
    auto sp1 = SharedPtr <int>(new int(42));
    std::cout << "sp1 use_count: " << sp1.use_count() << '\n';

    auto wp = sp1.weak_from_this();
    std::cout << "weak expired? " << wp.expired() << '\n';

    {
        auto sp2 = wp.lock();
        std::cout << "sp2 value: " << *sp2 << '\n';
        std::cout << "sp1 use_count: " << sp1.use_count() << '\n';
    } // sp2 离开作用域,计数减一

    std::cout << "sp1 use_count after sp2 destroyed: " << sp1.use_count() << '\n';
    sp1 = nullptr; // 资源释放
    std::cout << "wp expired? " << wp.expired() << '\n';
}

7. 进一步扩展

  1. 自定义内存池:在ControlBlock中引入自定义分配器,支持内存复用。
  2. 引用计数策略:可替换为轻量级计数(如 std::shared_ptruse_count 只保留 strong 计数)或更复杂的 thread-safe 方案。
  3. 回调机制:在资源销毁前调用用户提供的回调,适用于资源管理器模式。

通过上述实现,你可以在项目中使用自定义的智能指针,满足更细粒度的资源控制需求,同时保持与标准库接口相似的使用体验。

发表评论