shared_ptr是如何实现的?

参考回答

std::shared_ptr 是 C++ 标准库中的一种智能指针,用于管理动态分配的内存,并提供共享所有权的功能。它通过引用计数机制来管理资源,当最后一个 shared_ptr 被销毁时,它所管理的资源会自动释放。

详细讲解与拓展

  1. 引用计数机制

    std::shared_ptr 通过引用计数来管理资源。每次创建一个 shared_ptr 指向同一个对象时,引用计数增加;每次一个 shared_ptr 被销毁时,引用计数减少。当引用计数降到 0 时,表示没有 shared_ptr 再管理这个对象,资源会被自动释放。

    在实现上,shared_ptr 通常包含两个部分:

    • 指向对象的指针:即实际的资源(例如,通过 new 分配的内存)。
    • 引用计数:存储在某个地方(通常是一个共享的控制块),用于跟踪有多少个 shared_ptr 实例指向同一对象。

    示例:

    std::shared_ptr ptr1 = std::make_shared(10);
    std::shared_ptr ptr2 = ptr1;  // 引用计数增加
    
  2. 如何实现引用计数

    shared_ptr 的引用计数通常是由一个控制块来管理的。这个控制块存储了当前 shared_ptr 实例的数量以及与对象关联的资源的内存地址。

    每次 shared_ptr 被复制时,引用计数会增加;每次 shared_ptr 被销毁时,引用计数会减少。当引用计数降到 0 时,对象的内存会被释放。

    典型的实现大致如下:

    template 
    class shared_ptr {
    private:
       T* ptr;                // 指向对象的指针
       std::atomic* ref_count;  // 引用计数
    
    public:
       shared_ptr(T* ptr) : ptr(ptr), ref_count(new std::atomic(1)) {}
    
       shared_ptr(const shared_ptr& other) : ptr(other.ptr), ref_count(other.ref_count) {
           ++(*ref_count);  // 引用计数增加
       }
    
       ~shared_ptr() {
           if (--(*ref_count) == 0) {
               delete ptr;  // 当引用计数为 0 时,释放资源
               delete ref_count;
           }
       }
    };
    

    在这个简化版的实现中:

    • ptr 是指向实际资源的指针。
    • ref_count 是一个原子类型的引用计数,它确保在多线程环境中对引用计数的操作是安全的。
  3. 控制块

    实际的 std::shared_ptr 实现中,引用计数通常是与对象的实际数据分开存储的。为了解决多线程问题,引用计数会使用原子操作来增加和减少,以确保安全地进行引用计数操作。

    控制块的结构通常会包括以下内容:

    • 引用计数:记录有多少个 shared_ptr 正在使用这个对象。
    • 弱引用计数:记录有多少个 std::weak_ptr 观察这个对象。
    • 指向对象的指针:实际数据的内存地址。

    示例:

    struct ControlBlock {
       std::atomic strong_count;  // 强引用计数
       std::atomic weak_count;    // 弱引用计数
       T* ptr;                         // 实际对象的指针
    };
    

    当你复制一个 shared_ptr 时,strong_count 会增加。当 shared_ptr 被销毁时,strong_count 会减少,直到为零,这时才会删除资源。

  4. weak_ptrshared_ptr

    std::weak_ptr 是一种不增加引用计数的智能指针。它用于观察 shared_ptr 所管理的资源而不影响资源的生命周期。weak_ptr 会提供一个 lock() 方法来尝试获取一个有效的 shared_ptr,如果资源已经被释放,lock() 将返回一个空的 shared_ptr

    例如,假设你有两个相互引用的对象,使用 weak_ptr 可以避免循环引用的问题。

  5. 线程安全

    shared_ptr 的引用计数通常是线程安全的。在多线程环境中,shared_ptr 通过原子操作来管理引用计数,以确保多个线程安全地操作引用计数。然而,实际的资源访问(即通过 shared_ptr 获取资源)并不是自动线程安全的,程序员仍然需要使用适当的同步机制来确保对资源的安全访问。

总结

std::shared_ptr 通过引用计数机制来管理动态内存,确保在多个 shared_ptr 共享同一资源时,资源只有在最后一个 shared_ptr 被销毁时才会被释放。引用计数的增加和减少是通过控制块和原子操作来管理的,确保在多线程环境中的安全性。通过使用 std::shared_ptr,程序员无需手动管理内存,减少了内存泄漏的风险。

发表评论

后才能评论