C++:智能指针

        智能指针是C++中的一种对象,它像常规指针一样可以指向动态分配的内存,但在不再需要时可以自动释放该内存。这可以防止因忘记调用delete而导致的内存泄漏。C++标准库提供了三种类型的智能指针:std::unique_ptrstd::shared_ptrstd::weak_ptr.

  1. unique_ptr:唯一拥有其所指对象,没有引用计数。

  2. shared_ptr:允许多个智能指针共享同一个对象,使用引用计数来确保当最后一个对该对象的引用消失时,该对象会被正确地销毁。

  3. weak_ptr:是为了配合 shared_ptr 而提供的一种弱引用机制;它对所管理的对象不具有所有权。

        简单实现三种智能指针:

一:unique_ptr

template <typename T>
class unique_ptr 
{
public:
    explicit unique_ptr(T* ptr = nullptr) : m_ptr(ptr) {}

    ~unique_ptr() { delete m_ptr; }

    //移动构造函数 和 移动赋值运算符
    unique_ptr(unique_ptr&& other) : m_ptr(other.release()) {}

    unique_ptr& operator=(unique_ptr&& other) 
    {
        reset(other.release());
        return *this;
    }


    T& operator*() const { return *m_ptr; }

    T* operator->() const { return m_ptr; }

    //删除拷贝语义
    unique_ptr(const unique_ptr&) = delete;
    unique_ptr& operator=(const unique_ptr&) = delete;


    T* release() 
    {
        T* ptr = m_ptr;
        m_ptr = nullptr;
        return ptr;
    }

    void reset(T* ptr = nullptr)
    {
        delete m_ptr;
        m_ptr = ptr;
    }

private:
    T* m_ptr;
};

二:shared_ptr

struct ControlBlock 
{
    size_t strong_count;
    size_t weak_count;
    //考虑线程安全 增加:fetch_add(num)  减少:fetch_sub(num) 读取数据 load() 赋值 store(num)
    //std::atomic<size_t> strong_count;
    //std::atomic<size_t> weak_count;
};

template <typename T>
class shared_ptr {
public:
    shared_ptr() :m_ptr(nullptr), m_ctrl(nullptr) {}

    explicit shared_ptr(T* ptr = nullptr) : m_ptr(ptr), m_ctrl(ptr ? new ControlBlock{ 1, 0 } : nullptr) {}

    explicit shared_ptr(T* ptr, ControlBlock& controlBlock) :m_ptr(ptr), m_ctrl(controlBlock) {}

    shared_ptr(const weak_ptr<T>& weakptr) :m_ptr(weakptr.m_ptr), m_ctrl(weakptr.m_ctrl)
    {
        if (m_ctrl) m_ctrl->strong_count++;
    }

    ~shared_ptr() { decrease_and_destroy(); }

    shared_ptr(const shared_ptr& other) : m_ptr(other.m_ptr), m_ctrl(other.m_ctrl) 
    {
        if (m_ptr) ++m_ctrl->strong_count;
    }

    shared_ptr(shared_ptr&& other) noexcept :m_ptr(other.m_ptr), m_ctrl(other.m_ctrl)
    {
        other.m_ptr = nullptr;
        other.m_ctrl = nullptr;
    }

    shared_ptr& operator=(const shared_ptr& other) 
    {
        if (&other != this) 
        {
            if (other.m_ptr) ++other.m_ctrl->strong_count; 
            decrease_and_destroy(); 
            m_ptr = other.m_ptr;
            m_ctrl = other.m_ctrl;
        }
        return *this;
    }

    shared_ptr& operator=(shared_ptr&& other) noexcept
    {
        if (&other != this)
        {
            decrease_and_destroy();
            m_ptr = other.m_ptr;
            m_ctrl = other.m_ctrl;
            other.m_ctrl = nullptr;
            other.m_ptr = nullptr;
        }
        return *this;
    }

    static shared_ptr make_shared(T* ptr = nullptr)
    {
        //将指针和控制块分配到一块内存中
        struct ControlBlockWithObject : ControlBlock 
        {
            T object;
        };

        auto* ctrl = new ControlBlockWithObject;
        ctrl->strong_count = 1;
        ctrl->weak_count = 0;

        return shared_ptr(&ctrl->object, ctrl);
    }

    T& operator*() const { return *m_ptr; }
    T* operator->() const { return m_ptr; }

private:
    T* m_ptr;
    ControlBlock* m_ctrl;

    void decrease_and_destroy() 
    {
        if (m_ptr && --m_ctrl->strong_count == 0) 
        {
            delete m_ptr;
            if (m_ctrl->weak_count == 0) delete m_ctrl;
        }
    }
};

三:weak_ptr

template <typename T>
class weak_ptr {
public:
    weak_ptr() : m_ptr(nullptr), m_ctrl(nullptr) {}

    weak_ptr(const shared_ptr<T>& spt) : m_ptr(spt.m_ptr), m_ctrl(spt.m_ctrl) 
    {
        if (m_ctrl) ++m_ctrl->weak_count;
    }

    ~weak_ptr() { decrease(); }

    weak_ptr(const weak_ptr& other) : m_ptr(other.m_ptr), m_ctrl(other.m_ctrl) 
    {
        if (m_ctrl) ++m_ctrl->weak_count;
    }

    weak_ptr& operator=(const weak_ptr& other) 
    {
        if (&other != this) 
        {
            if (m_ctrl) ++m_ctrl->weak_count;
            decrease();
            m_ptr = other.m_ptr;
            m_ctrl = other.m_ctrl;
        }
        return *this;
    }

    shared_ptr<T> lock() const 
    {
        if (m_ctrl && m_ctrl->strong_count > 0) return shared_ptr<T>(*this); 
        else return shared_ptr<T>();
    }

private:
    T* m_ptr;
    ControlBlock* m_ctrl;

    void decrease() 
    {
        if (m_ctrl && --m_ctrl->weak_count == 0 && m_ctrl->strong_count == 0) delete m_ctrl;
    }
};

        对于 std::shared_ptr, 它内部实现了一个控制块,这个控制块包含了两个计数器:

  • 一个是 shared 引用计数
  • 另一个是 weak 引用计数

        当我们创建一个新的 shared_ptr 或者复制现有的 shared_ptr, shared 引用计数会增加。当 shared_ptr 被销毁或者超出作用域, shared 计数器就会减少。只有当 shared 计数器为0时, 才会删除所管理资源(即调用 delete)。

        同时,每次创建或复制 weak_ptr, weak 引用计数就增加; 每次销毁或超出作用域, weak 计数器就减少。只有当两个计数器都为0时, 才会删除控制块本身。

   std::shared_ptr的引用计数通常存储在动态分配的控制块中。这个控制块是在创建std::shared_ptr实例时由标准库自动分配的。这个控制块不仅包含引用计数,还可能包含被管理对象的析构函数和其他一些必要信息。

   std::shared_ptrstd::weak_ptr都是C++11引入的智能指针类型,它们共享同一个控制块,这个控制块中包含了被管理对象的析构函数、共享引用计数(shared reference count)和弱引用计数(weak reference count)。

  • 共享引用计数:记录了有多少个 std::shared_ptr 实例正在指向同一个对象。当最后一个 std::shared_ptr 被销毁或者超出作用域时(即共享引用计数变为0),那么被管理的对象就会被自动删除。

  • 弱引用计数:记录了有多少个 std::weak_ptr 实例正在指向同一个对象。不过与 std::shared_ptr 不同, std::weak_ptr 不会延长目标对象的生命周期。也就是说,即使还有活跃的 std::weak_ptr, 目标对象也可能已经被销毁。

        为什么需要两种不同类型的引用计数呢?

        原因在于防止循环引用导致内存泄漏,并且让弱指针可以安全地访问资源。如果只有共享引用计数而没有弱引用计数,在一些情况下可能会导致内存泄漏。例如,在某些数据结构(如双向链表或图)中,节点之间可能形成循环依赖关系,这样每个节点都无法被释放。

        此外,当我们需要创建一个不拥有资源但需要观察资源状态的智能指针时(比如检查资源是否还存在),我们可以使用 std::weak_ptr。在转换 weak_ptr 为 shared_ptr 时, 如果 shared 引用计数已经为0 (意味着资源已经释放), 那么得到的 shared_ptr 将为空 (null), 这样就避免了悬挂指针问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值