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

在现代 C++ 开发中,智能指针(如 std::unique_ptr 和 std::shared_ptr)已经成为管理动态资源的重要工具。然而,在某些特殊场景下,标准库的智能指针可能无法满足所有需求,例如需要自定义引用计数策略、支持多继承或在多线程环境下实现细粒度的锁机制。下面我们将演示如何从头开始实现一个简易的自定义智能指针 MySmartPtr,并讨论其扩展点与使用场景。

1. 目标与设计原则

  • 自动资源管理:当指针生命周期结束时自动销毁底层资源。
  • 引用计数:支持多份指针共享同一资源,并在最后一份销毁时释放资源。
  • 线程安全:采用原子操作确保多线程环境下计数器的正确性。
  • 可定制销毁器:允许用户指定自定义销毁函数,以满足非 delete 的资源释放方式。

2. 基本实现

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

template <typename T, typename Deleter = std::default_delete<T>>
class MySmartPtr {
public:
    // 构造器
    explicit MySmartPtr(T* ptr = nullptr, Deleter del = Deleter())
        : ptr_(ptr), refcnt_(new std::atomic <size_t>(1)), deleter_(del) {}

    // 拷贝构造
    MySmartPtr(const MySmartPtr& other)
        : ptr_(other.ptr_), refcnt_(other.refcnt_), deleter_(other.deleter_) {
        if (ptr_) (*refcnt_)++;
    }

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

    // 拷贝赋值
    MySmartPtr& operator=(const MySmartPtr& other) {
        if (this != &other) {
            release();                 // 先释放当前资源
            ptr_ = other.ptr_;
            refcnt_ = other.refcnt_;
            deleter_ = other.deleter_;
            if (ptr_) (*refcnt_)++;    // 增加引用计数
        }
        return *this;
    }

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

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

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

    // 访问引用计数
    size_t use_count() const { return ptr_ ? (*refcnt_) : 0; }

    // 互斥释放
private:
    void release() {
        if (ptr_ && refcnt_) {
            if (--(*refcnt_) == 0) {
                deleter_(ptr_);
                delete refcnt_;
            }
            ptr_ = nullptr;
            refcnt_ = nullptr;
        }
    }

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

关键点说明

  1. 引用计数:使用 `std::atomic ` 保证多线程环境下计数的原子性。
  2. 自定义销毁器Deleter 模板参数允许用户传入任何可调用对象,例如 std::function<void(T*)> 或自定义结构。
  3. 移动语义:移动构造和赋值时将指针所有权转移,避免不必要的引用计数递增递减。
  4. 资源释放release() 方法在引用计数降为零时调用销毁器并删除计数器。

3. 使用示例

int main() {
    MySmartPtr <int> p1(new int(42));
    std::cout << "p1 use_count: " << p1.use_count() << std::endl; // 1

    MySmartPtr <int> p2 = p1; // 拷贝
    std::cout << "p1 use_count: " << p1.use_count() << std::endl; // 2

    {
        MySmartPtr <int> p3 = std::move(p2); // 移动
        std::cout << "p3 value: " << *p3 << std::endl;
    } // p3 离开作用域,计数减一

    std::cout << "p1 use_count after p3 destruct: " << p1.use_count() << std::endl; // 1

    return 0;
}

4. 可扩展方向

  1. 弱引用:实现类似 std::weak_ptr 的弱引用,避免循环引用导致的内存泄漏。
  2. 自定义分配器:允许用户在构造时传入自定义分配器,以支持内存池或共享内存。
  3. 多继承支持:在存在虚继承的类层次结构中使用 static_castdynamic_cast 进行安全转换。
  4. 性能优化:采用分段计数或 std::shared_ptr 的“自定义计数器”机制减少锁竞争。

5. 与标准库的比较

特性 std::shared_ptr MySmartPtr
线程安全
自定义销毁器 支持 支持
弱引用 支持 暂无
内存占用 3 sizeof(void) 2 sizeof(void)(更小)
对齐 & 对象池 不可定制 可定制
典型使用 一般共享 特殊需求(如自定义分配器、低内存占用)

6. 小结

通过上面的实现,我们掌握了从零开始创建智能指针的基本思路。虽然标准库已经提供了成熟的 std::shared_ptrstd::unique_ptr,但在需要特殊行为或更细粒度控制时,自己实现一个自定义智能指针仍是非常有价值的练习。希望这篇文章能帮助你在 C++ 编程中更好地理解资源管理与多线程安全的底层实现细节。

发表评论