如何在C++中实现自定义的智能指针?

在 C++ 标准库中,std::shared_ptrstd::unique_ptr 已经满足大多数资源管理需求,但有时我们需要一种更符合业务需求的自定义智能指针。下面将演示一个简易但功能完整的 MySharedPtr 实现,包括引用计数、线程安全、以及自定义删除器。代码仅供学习参考,实际生产环境请使用标准库或成熟的第三方实现。

#pragma once
#include <atomic>
#include <cstddef>
#include <memory>
#include <iostream>
#include <utility>

template<typename T, typename Deleter = std::default_delete<T>>
class MySharedPtr {
public:
    // 构造函数:直接接受裸指针
    explicit MySharedPtr(T* ptr = nullptr, Deleter del = Deleter())
        : ptr_(ptr), deleter_(del) {
        if (ptr_) {
            ref_count_ = new std::atomic <size_t>(1);
        } else {
            ref_count_ = nullptr;
        }
    }

    // 构造函数:接受已有的 MySharedPtr(复制构造)
    MySharedPtr(const MySharedPtr& other) noexcept
        : ptr_(other.ptr_), deleter_(other.deleter_), ref_count_(other.ref_count_) {
        increment();
    }

    // 移动构造
    MySharedPtr(MySharedPtr&& other) noexcept
        : ptr_(other.ptr_), deleter_(std::move(other.deleter_)), ref_count_(other.ref_count_) {
        other.ptr_ = nullptr;
        other.ref_count_ = nullptr;
    }

    // 赋值运算符(拷贝)
    MySharedPtr& operator=(const MySharedPtr& other) noexcept {
        if (this != &other) {
            release();
            ptr_ = other.ptr_;
            deleter_ = other.deleter_;
            ref_count_ = other.ref_count_;
            increment();
        }
        return *this;
    }

    // 赋值运算符(移动)
    MySharedPtr& operator=(MySharedPtr&& other) noexcept {
        if (this != &other) {
            release();
            ptr_ = other.ptr_;
            deleter_ = std::move(other.deleter_);
            ref_count_ = other.ref_count_;
            other.ptr_ = nullptr;
            other.ref_count_ = nullptr;
        }
        return *this;
    }

    ~MySharedPtr() { release(); }

    // 解引用操作
    T& operator*() const noexcept { return *ptr_; }
    T* operator->() const noexcept { return ptr_; }
    T* get() const noexcept { return ptr_; }

    // 引用计数
    size_t use_count() const noexcept { return ref_count_ ? ref_count_->load() : 0; }

    // 判断是否唯一所有者
    bool unique() const noexcept { return use_count() == 1; }

private:
    void increment() noexcept {
        if (ref_count_) ref_count_->fetch_add(1, std::memory_order_relaxed);
    }

    void release() noexcept {
        if (ref_count_ && ref_count_->fetch_sub(1, std::memory_order_acq_rel) == 1) {
            deleter_(ptr_);
            delete ref_count_;
        }
    }

    T* ptr_;
    Deleter deleter_;
    std::atomic <size_t>* ref_count_;
};

关键点说明

  1. 引用计数

    • 使用 `std::atomic ` 以保证多线程环境下计数安全。
    • fetch_add/fetch_submemory_order_relaxedacq_rel 保证正确的同步。
  2. 自定义删除器

    • 模板参数 Deleter 默认使用 `std::default_delete `。
    • 允许用户传入 lambda 或自定义 functor 来控制资源释放逻辑,例如:MySharedPtr<MyFile, FileCloser>(filePtr, FileCloser{})
  3. 构造/析构/赋值

    • 复制构造时计数加 1;移动构造时转移所有权。
    • 赋值运算符先释放自身资源,再做复制或移动。
  4. 使用方式

    struct MyStruct { int x; };
    MySharedPtr <MyStruct> p1(new MyStruct{10});
    MySharedPtr <MyStruct> p2 = p1;            // 共享所有权
    std::cout << p1.use_count() << std::endl; // 输出 2

与标准库的区别

  • 性能:标准库实现使用控制块来分离数据和引用计数,进一步优化。
  • 异常安全:标准库保证异常安全,本文示例简化了错误处理。
  • 特性:标准库提供 weak_ptr、自定义控制块、make_shared 等高级功能。

适用场景

  • 学习指针管理和 RAII 的实现细节。
  • 在不允许引入 ` `(如古老编译器或特殊项目)时使用。
  • 需要特定的删除逻辑但不想使用标准库的完整实现。

小结

自定义智能指针可以帮助我们更深入理解 C++ 内存管理机制。通过模板化的删除器、线程安全的引用计数以及完整的生命周期管理,MySharedPtr 能够在多种环境下安全地共享资源。若项目规模较大或需要更完善的功能,建议直接使用 std::shared_ptr,以减少维护成本和潜在 bug。

发表评论