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

在 C++11 之前,智能指针(如 std::unique_ptrstd::shared_ptr)并不存在,程序员往往需要手动管理资源。即便在 C++11 之后,这些标准库实现已非常成熟,但在某些特殊场景下,我们可能需要一个定制的智能指针来满足特殊的生命周期、线程安全、或日志记录需求。下面将展示一个简易但功能完整的自定义智能指针实现,名称为 MySharedPtr,实现了引用计数、共享与移动语义,并提供了对多线程环境的基本保护。


1. 设计思路

  1. 引用计数:使用 std::atomic<std::size_t> 维护共享对象的引用计数,保证线程安全。
  2. 原始指针T* 用于存储实际资源。
  3. 构造/析构:构造时初始化计数为 1,析构时递减计数,计数为 0 时销毁资源。
  4. 拷贝/移动语义:拷贝构造/赋值增加计数;移动构造/赋值转移指针与计数,避免无效指针。
  5. 访问:重载 operator*operator->operator bool
  6. 辅助功能:提供 use_count()reset()

2. 代码实现

#include <atomic>
#include <iostream>
#include <utility>
#include <cassert>

template <typename T>
class MySharedPtr {
public:
    // 默认构造
    MySharedPtr() noexcept : ptr_(nullptr), count_(nullptr) {}

    // 构造传入原始指针
    explicit MySharedPtr(T* ptr) : ptr_(ptr), count_(new std::atomic<std::size_t>(1)) {}

    // 拷贝构造
    MySharedPtr(const MySharedPtr& other) noexcept
        : ptr_(other.ptr_), count_(other.count_) {
        increment();
    }

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

    // 析构
    ~MySharedPtr() {
        decrement();
    }

    // 拷贝赋值
    MySharedPtr& operator=(const MySharedPtr& other) noexcept {
        if (this != &other) {
            decrement();
            ptr_ = other.ptr_;
            count_ = other.count_;
            increment();
        }
        return *this;
    }

    // 移动赋值
    MySharedPtr& operator=(MySharedPtr&& other) noexcept {
        if (this != &other) {
            decrement();
            ptr_ = other.ptr_;
            count_ = other.count_;
            other.ptr_ = nullptr;
            other.count_ = nullptr;
        }
        return *this;
    }

    // 访问运算符
    T& operator*() const noexcept { assert(ptr_); return *ptr_; }
    T* operator->() const noexcept { assert(ptr_); return ptr_; }
    explicit operator bool() const noexcept { return ptr_ != nullptr; }

    // 用于调试查看引用计数
    std::size_t use_count() const noexcept {
        return count_ ? *count_ : 0;
    }

    // 重置为nullptr,或重新指向新的对象
    void reset(T* ptr = nullptr) noexcept {
        if (ptr_ != ptr) {
            decrement();
            ptr_ = ptr;
            count_ = (ptr ? new std::atomic<std::size_t>(1) : nullptr);
        }
    }

private:
    T* ptr_;
    std::atomic<std::size_t>* count_;

    void increment() noexcept {
        if (count_) ++(*count_);
    }

    void decrement() noexcept {
        if (count_ && --(*count_) == 0) {
            delete ptr_;
            delete count_;
        }
    }
};

3. 使用示例

struct Widget {
    Widget() { std::cout << "Widget constructed\n"; }
    ~Widget() { std::cout << "Widget destructed\n"; }
    void greet() const { std::cout << "Hello from Widget!\n"; }
};

int main() {
    MySharedPtr <Widget> sp1(new Widget());   // use_count = 1
    std::cout << "sp1 count: " << sp1.use_count() << '\n';

    {
        MySharedPtr <Widget> sp2 = sp1;       // use_count = 2
        std::cout << "sp2 count: " << sp2.use_count() << '\n';
        sp2->greet();

        MySharedPtr <Widget> sp3 = std::move(sp1);  // sp1 becomes null, sp3 count = 2
        std::cout << "sp3 count: " << sp3.use_count() << '\n';
        std::cout << "sp1 bool: " << static_cast<bool>(sp1) << '\n';
    } // sp2, sp3 out of scope, count decremented

    std::cout << "After block, sp1 count: " << sp1.use_count() << '\n';
    return 0;
}

运行结果(示例):

Widget constructed
sp1 count: 1
sp2 count: 2
Hello from Widget!
sp3 count: 2
sp1 bool: 0
Widget destructed
After block, sp1 count: 0

4. 关键点说明

  1. 线程安全:使用 std::atomic 让引用计数的递增递减操作在多线程中无数据竞争。
  2. 资源管理:当计数降至零时,删除原始指针与计数器,防止内存泄漏。
  3. 移动语义:移动构造/赋值后,原始对象的指针变为 nullptr,保证不产生额外引用计数。
  4. 异常安全:所有操作均 noexcept,符合标准库实现的行为。

5. 小结

通过上述实现,你可以得到一个与 std::shared_ptr 功能相似的自定义智能指针,并可在此基础上继续扩展,例如:

  • 加入自定义删除器(类似 std::unique_ptr 的 deleter)。
  • 提供 `make_my_shared ()` 工厂函数,避免两次 `new`。
  • 实现弱引用 MyWeakPtr,以避免循环引用。

此自定义智能指针既展示了 C++ 资源管理的核心理念,也为在特殊需求场景下的进一步定制提供了坚实基础。

发表评论