shared_ptr是如何实现的?
参考回答
std::shared_ptr
是 C++ 标准库中的一种智能指针,用于管理动态分配的内存,并提供共享所有权的功能。它通过引用计数机制来管理资源,当最后一个 shared_ptr
被销毁时,它所管理的资源会自动释放。
详细讲解与拓展
- 引用计数机制
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; // 引用计数增加 - 指向对象的指针:即实际的资源(例如,通过
- 如何实现引用计数
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
是一个原子类型的引用计数,它确保在多线程环境中对引用计数的操作是安全的。
- 控制块
实际的
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
会减少,直到为零,这时才会删除资源。 - 引用计数:记录有多少个
weak_ptr
与shared_ptr
std::weak_ptr
是一种不增加引用计数的智能指针。它用于观察shared_ptr
所管理的资源而不影响资源的生命周期。weak_ptr
会提供一个lock()
方法来尝试获取一个有效的shared_ptr
,如果资源已经被释放,lock()
将返回一个空的shared_ptr
。例如,假设你有两个相互引用的对象,使用
weak_ptr
可以避免循环引用的问题。- 线程安全
shared_ptr
的引用计数通常是线程安全的。在多线程环境中,shared_ptr
通过原子操作来管理引用计数,以确保多个线程安全地操作引用计数。然而,实际的资源访问(即通过shared_ptr
获取资源)并不是自动线程安全的,程序员仍然需要使用适当的同步机制来确保对资源的安全访问。
总结
std::shared_ptr
通过引用计数机制来管理动态内存,确保在多个 shared_ptr
共享同一资源时,资源只有在最后一个 shared_ptr
被销毁时才会被释放。引用计数的增加和减少是通过控制块和原子操作来管理的,确保在多线程环境中的安全性。通过使用 std::shared_ptr
,程序员无需手动管理内存,减少了内存泄漏的风险。