C++中的智能指针std::shared_ptr是线程安全的吗?以及它的详细实现原理
目录
1.1.基础用法:创建与初始化
1.2.共享所有权:复制与赋值
1.3.访问与操作管理的资源
1.4.释放资源:reset() 与析构
1.5.自定义删除器:管理非内存资源
1.6.与 weak_ptr 配合:解决循环引用
2.std::weak_ptr的使用
2.1.核心特性与定位
2.2.基本用法:创建与转换
2.3.典型应用场景
2.4.注意事项
3.1.总体结构
3.2._Ref_count_base实现分析
3.2.1.强引用计数的增减方法
3.2.2.观察者模式中 weak_ptr 与 _Ref_count_base 的联动逻辑
3.2.3.核心优势:_Ref_count_base 如何解决观察者模式的痛点
3.3._Ref_count实现分析
3.4._Ref_count_resource实现分析
3.4.1.核心定位与适用场景
3.4.2.关键成员与构造函数
3.4.3.核心重写函数:释放逻辑的实现
3.4.4.关键优势与约束
3.5._Ref_count_resource_alloc实现分析
3.5.1.核心定位与适用场景
3.5.2.关键成员与构造函数
3.5.3.核心重写函数:释放逻辑的实现
3.5.4.核心优势与约束
3.6._Ref_count_obj2实现分析
3.6.1.核心定位与适用场景
3.6.2.关键成员:嵌入对象的存储 union
3.6.3.构造函数:原地构造对象(核心逻辑)
3.6.4.重写函数:释放逻辑适配 “嵌入对象”
3.6.5.核心优势与约束
3.7._Ref_count_obj_alloc3实现分析
3.7.1.核心定位与适用场景
3.7.2.关键成员与继承逻辑
3.7.3.构造函数:分配器 + 原地构造对象
3.7.4.重写函数:释放逻辑适配 “分配器 + 嵌入对象”
3.7.5.应用场景
3.7.6.核心优势与约束
3.7.7.与其他控制块类的核心区别
3.8.1.继承关系与核心成员
3.8.2.构造函数:控制块的创建与初始化
3.8.3.析构函数与引用计数递减
3.8.4.reset操作:所有权转移
3.8.5.控制块的具体类型
3.10.1.核心定位与设计目标
3.10.2.关键成员与友元关系
3.10.3.核心机制:_Wptr 如何关联到控制块?
3.10.4.使用限制与注意事项
1.std::shared_ptr的使用
std::shared_ptr 是 C++ 中用于共享资源所有权的智能指针,其核心是通过引用计数自动管理资源生命周期(当最后一个持有者销毁时释放资源)。
1.1.基础用法:创建与初始化
shared_ptr 的创建有多种方式,优先推荐 std::make_shared(性能更优,一次分配资源和控制块内存)。为什么优先推荐std::make_shared,下面仔细讲原因。
1.用 std::make_shared 创建(推荐)
make_shared 直接在堆上构造对象,并返回关联的 shared_ptr,避免单独 new 导致的两次内存分配(对象 + 控制块)。
#include <memory> // 包含 shared_ptr 和 make_shared
#include <iostream>int main() {// 创建一个管理 int(42) 的 shared_ptrstd::shared_ptr<int> ptr1 = std::make_shared<int>(42);std::cout << "ptr1 管理的值:" << *ptr1 << std::endl; // 输出:42// 创建一个管理自定义类型的 shared_ptrstruct MyClass {int value;MyClass(int v) : value(v) {}};std::shared_ptr<MyClass> ptr2 = std::make_shared<MyClass>(100);std::cout << "ptr2 管理的对象值:" << ptr2->value << std::endl; // 输出:100return 0;
}
2.用原始指针构造(不推荐)
通过 new 创建对象后传递给 shared_ptr 构造函数,会导致两次内存分配(对象 + 控制块),性能略差。
// 不推荐:两次内存分配
std::shared_ptr<int> ptr3(new int(200)); // 管理 int(200)
警告:绝对不要用同一个原始指针创建多个 shared_ptr,否则会导致资源被多次释放(未定义行为):
int* raw_ptr = new int(300);
std::shared_ptr<int> ptr4(raw_ptr);
std::shared_ptr<int> ptr5(raw_ptr); // 错误!ptr4 和 ptr5 会各自释放 raw_ptr,导致二次释放
1.2.共享所有权:复制与赋值
多个 shared_ptr 可共享同一资源,引用计数会自动同步(原子操作,线程安全)。
1.复制(引用计数 +1)
std::shared_ptr<int> ptr_a = std::make_shared<int>(50);
std::shared_ptr<int> ptr_b = ptr_a; // 复制:ptr_a 和 ptr_b 共享资源,引用计数=2// 查看引用计数(仅用于调试,性能开销大)
std::cout << "ptr_a 引用计数:" << ptr_a.use_count() << std::endl; // 输出:2
std::cout << "ptr_b 引用计数:" << ptr_b.use_count() << std::endl; // 输出:2
2.赋值(旧资源计数 -1,新资源计数 +1)
std::shared_ptr<int> ptr_c = std::make_shared<int>(10);
std::shared_ptr<int> ptr_d = std::make_shared<int>(20);ptr_d = ptr_c; // ptr_d 放弃旧资源(计数-1至0,释放),转而共享 ptr_c 的资源(计数+1至2)
1.3.访问与操作管理的资源
通过 *、-> 访问对象(类似原始指针),通过 get() 获取原始指针(谨慎使用)。
1.解引用操作
std::shared_ptr<int> ptr = std::make_shared<int>(30);
*ptr = 40; // 修改管理的对象值(通过 operator*)
std::cout << *ptr << std::endl; // 输出:40struct Data { int x; };
std::shared_ptr<Data> data_ptr = std::make_shared<Data>();
data_ptr->x = 100; // 通过 operator-> 访问成员
std::cout << data_ptr->x << std::endl; // 输出:100
2.获取原始指针(get())
get() 返回管理的原始指针,禁止手动 delete(否则 shared_ptr 会再次释放,导致未定义行为)。在使用的时候不建议直接获取原始指针操作,因为这样就脱离了管控,不可控,可以通过
get_self_from
std::shared_ptr<int> ptr = std::make_shared<int>(50);
int* raw = ptr.get(); // 获取原始指针
std::cout << *raw << std::endl; // 输出:50(正确)
// delete raw; // 错误!会导致 ptr 释放时二次删除
1.4.释放资源:reset() 与析构
shared_ptr 超出作用域或调用 reset() 时,会自动递减引用计数,计数为 0 时释放资源。
1.reset() 方法
- 不带参数:放弃当前资源所有权(计数 -1),变为空指针。
- 带参数:放弃当前资源,转而管理新资源(新资源计数 +1)。
std::shared_ptr<int> ptr = std::make_shared<int>(100);
ptr.reset(); // 引用计数 -1 至 0,释放资源,ptr 变为空
if (!ptr) {std::cout << "ptr 为空" << std::endl; // 输出:ptr 为空
}ptr.reset(new int(200)); // 管理新资源(int(200)),计数=1
2.析构自动释放
当 shared_ptr 超出作用域时,析构函数会自动调用 _Decref() 递减计数,计数为 0 则释放资源:
{std::shared_ptr<int> ptr = std::make_shared<int>(300); // 计数=1
} // 超出作用域,析构 ptr,计数-1至0,资源释放
1.5.自定义删除器:管理非内存资源
shared_ptr 支持自定义删除器(函数、lambda、函数对象),用于释放文件句柄、网络连接等非内存资源。
示例:管理文件指针(用 fclose 作为删除器)
#include <cstdio> // 包含 fopen, fcloseint main() {// 自定义删除器:关闭文件并释放指针auto file_deleter = [](FILE* fp) {if (fp) {std::fclose(fp); // 关闭文件std::cout << "文件已关闭" << std::endl;}};// 创建 shared_ptr,关联文件指针和自定义删除器FILE* fp = std::fopen("test.txt", "w");std::shared_ptr<FILE> file_ptr(fp, file_deleter); // 计数=1// 使用文件(无需手动关闭,file_ptr 析构时自动调用删除器)if (file_ptr) {std::fputs("hello", file_ptr.get());}return 0; // file_ptr 析构,计数-1至0,调用 file_deleter 关闭文件
}
2.数组支持(C++17 及以上)
C++17 开始,shared_ptr 原生支持动态数组,自动使用 delete[] 释放,可通过 operator[] 访问元素。
// C++17 及以上:管理数组
std::shared_ptr<int[]> arr_ptr = std::make_shared<int[]>(3); // 3个int的数组
arr_ptr[0] = 10;
arr_ptr[1] = 20;
arr_ptr[2] = 30;
std::cout << arr_ptr[1] << std::endl; // 输出:20
C++17 之前需手动指定删除器:
// C++17 之前的数组管理
std::shared_ptr<int> old_arr_ptr(new int[3], [](int* p) { delete[] p; });
1.6.与 weak_ptr 配合:解决循环引用
当两个 shared_ptr 互相引用(循环引用)时,引用计数无法归零,导致内存泄漏。此时需用 weak_ptr(不增加引用计数的 “观察者”)打破循环。
示例:双向链表节点(用 weak_ptr 打破循环)
#include <memory>struct Node {int value;std::shared_ptr<Node> prev; // 前向强引用std::weak_ptr<Node> next; // 后向弱引用(打破循环)~Node() { std::cout << "Node " << value << " 已释放" << std::endl; }
};int main() {auto node1 = std::make_shared<Node>(1);auto node2 = std::make_shared<Node>(2);node1->next = node2; // weak_ptr 赋值,node2 计数仍为1node2->prev = node1; // shared_ptr 赋值,node1 计数变为2// 离开作用域:node1 和 node2 计数递减至0,正常释放return 0;
}
// 输出:
// Node 2 已释放
// Node 1 已释放
1.7.利用std::enable_shared_from_this返回自身
在 C++ 中,若想让类的成员函数返回一个指向当前对象自身的 shared_ptr,直接使用 return shared_ptr<MyClass>(this) 是错误的(会导致重复释放)。正确的做法是让类继承 std::enable_shared_from_this,并通过 shared_from_this() 方法安全返回自身的 shared_ptr。
若直接用 this 指针构造 shared_ptr,会创建一个独立的引用计数控制块,与原有管理该对象的 shared_ptr 不共享计数。例如:
class MyClass {
public:shared_ptr<MyClass> get_self() {return shared_ptr<MyClass>(this); // 错误!}
};int main() {auto sp1 = make_shared<MyClass>(); // 控制块 A:_Uses=1auto sp2 = sp1->get_self(); // 控制块 B:_Uses=1(独立计数)// 析构时,sp1 和 sp2 分别释放对象 → 重复释放,未定义行为(崩溃)
}
原因:sp1 和 sp2 会认为自己是对象的唯一管理者,析构时都会调用 delete this,导致对象被释放两次。
正确做法:继承 std::enable_shared_from_this
std::enable_shared_from_this<T> 是一个模板基类,内部维护了一个 weak_ptr<T>,用于跟踪管理当前对象的 shared_ptr。当对象被 shared_ptr 管理时,这个 weak_ptr 会绑定到对应的控制块,通过 shared_from_this() 可返回一个共享计数的 shared_ptr。
示例如下:
#include <memory>class MyClass : public std::enable_shared_from_this<MyClass> { // 继承基类
public:std::shared_ptr<MyClass> get_self() {return shared_from_this(); // 安全返回自身的 shared_ptr}
};int main() {auto sp1 = std::make_shared<MyClass>(); // 必须先用 shared_ptr 管理对象auto sp2 = sp1->get_self(); // sp2 与 sp1 共享计数(_Uses=2)// 析构时,sp1 和 sp2 递减计数,最终 _Uses=0 释放对象(仅一次)return 0;
}
2.std::weak_ptr的使用
std::weak_ptr 是 C++ 中配合 std::shared_ptr 使用的 “弱引用” 智能指针,其核心特点是不拥有资源所有权(不增加 shared_ptr 的引用计数),仅作为 “观察者” 跟踪资源的存活状态。它主要用于解决 shared_ptr 的循环引用问题,或在不影响资源生命周期的前提下观察资源。
2.1.核心特性与定位
- 不增加引用计数:
weak_ptr指向shared_ptr管理的资源时,不会使shared_ptr的use_count递增,因此不影响资源的释放时机。 - 不能直接访问资源:
weak_ptr没有operator*或operator->,必须通过lock()方法转换为shared_ptr后才能访问资源(确保访问时资源未被释放)。 - 跟踪资源存活:通过
expired()方法可判断所指向的资源是否已被释放(use_count() == 0)。
2.2.基本用法:创建与转换
weak_ptr 通常从 shared_ptr 构造,无法直接管理原始指针(无接受原始指针的构造函数)。
1.创建 weak_ptr(从 shared_ptr 构造)
#include <memory>
#include <iostream>int main() {// 1. 创建 shared_ptr 管理资源std::shared_ptr<int> sp = std::make_shared<int>(100);std::cout << "sp 的引用计数:" << sp.use_count() << std::endl; // 输出:1// 2. 从 shared_ptr 构造 weak_ptr(不增加引用计数)std::weak_ptr<int> wp(sp);std::cout << "sp 的引用计数(构造 wp 后):" << sp.use_count() << std::endl; // 输出:1(计数未变)return 0;
}
2.通过 lock() 访问资源(转换为 shared_ptr)
weak_ptr::lock() 是访问资源的唯一安全方式:
- 若资源存活(
use_count() > 0),返回一个指向该资源的shared_ptr(引用计数 +1)。 - 若资源已释放(
use_count() == 0),返回空shared_ptr。
std::shared_ptr<int> sp = std::make_shared<int>(200);
std::weak_ptr<int> wp(sp);// 尝试获取 shared_ptr 访问资源
if (std::shared_ptr<int> locked = wp.lock()) { // locked 非空表示资源存活std::cout << "资源值:" << *locked << std::endl; // 输出:200std::cout << "当前引用计数:" << locked.use_count() << std::endl; // 输出:2(sp 和 locked 共享)
} else {std::cout << "资源已释放" << std::endl;
}
3.检查资源是否存活(expired())
weak_ptr::expired() 用于判断资源是否已被释放(本质是检查 use_count() == 0):
std::shared_ptr<int> sp = std::make_shared<int>(300);
std::weak_ptr<int> wp(sp);std::cout << "资源是否存活(sp 有效时):" << !wp.expired() << std::endl; // 输出:1(true)sp.reset(); // 释放资源,引用计数变为 0
std::cout << "资源是否存活(sp 释放后):" << !wp.expired() << std::endl; // 输出:0(false)
注意:expired() 和 lock() 结合使用时,需警惕竞态条件(多线程下 expired() 返回 false 后,lock() 仍可能返回空,因其他线程可能在中间释放资源)。因此,优先直接使用 lock() 并判断返回值,而非先调用 expired()。
2.3.典型应用场景
1.解决 shared_ptr 的循环引用(核心场景)
当两个 shared_ptr 互相引用时,引用计数无法归零导致内存泄漏。用 weak_ptr 替代其中一个方向的 shared_ptr,可打破循环。示例1.6章节讲过,可以返回阅读。
2.缓存场景:临时观察资源,不阻止释放
在缓存系统中,weak_ptr 可用于临时缓存资源:当资源被外部 shared_ptr 持有(正在使用)时,缓存有效;当外部不再使用(shared_ptr 释放),缓存自动失效,不占用资源。
#include <memory>
#include <unordered_map>
#include <string>// 缓存:用 weak_ptr 存储资源,不影响其释放
std::unordered_map<std::string, std::weak_ptr<int>> cache;// 添加资源到缓存
void add_to_cache(const std::string& key, std::shared_ptr<int> resource) {cache[key] = resource; // 存储 weak_ptr,不增加计数
}// 从缓存获取资源(若存活)
std::shared_ptr<int> get_from_cache(const std::string& key) {if (auto it = cache.find(key); it != cache.end()) {return it->second.lock(); // 转换为 shared_ptr,存活则返回,否则返回空}return nullptr;
}int main() {{auto res = std::make_shared<int>(100);add_to_cache("key1", res); // 缓存 res 的 weak_ptrstd::cout << "缓存中获取:" << *get_from_cache("key1") << std::endl; // 输出:100} // res 销毁,资源释放// 资源已释放,缓存失效if (auto cached = get_from_cache("key1")) {std::cout << "缓存有效" << std::endl;} else {std::cout << "缓存失效(资源已释放)" << std::endl; // 输出此句}return 0;
}
3.观察者模式:避免观察者延长被观察者生命周期
在观察者模式中,被观察者通常不需要被观察者 “持有” 而延长生命周期。用 weak_ptr 存储观察者指针,可避免被观察者释放后,观察者仍引用它导致的内存问题。
2.4.注意事项
1)不能直接访问资源:weak_ptr 没有 * 和 -> 操作符,必须通过 lock() 转换为 shared_ptr 后才能访问,否则可能访问已释放的内存。
2)线程安全限制:weak_ptr 的 lock()、expired() 等操作本身不是线程安全的。若多个线程同时操作同一个 weak_ptr(如同时调用 lock()),需通过互斥锁同步。
3)控制块依赖:weak_ptr 依赖 shared_ptr 的控制块(存储 weak_count),即使资源已释放,控制块也会在最后一个 weak_ptr 销毁后才释放。
3.std::shared_ptr的具体实现原理
这里以微软的vs2022的std::shared_ptr(C++20)实现为例进行分析。
3.1.总体结构
通过阅读源码整理出的类图如下:

shared_ptr继承自_Ptr_base<_Ty>(代码中using _Mybase = _Ptr_base<_Ty>),_Ptr_base是智能指针的基础类,通常包含两个核心成员(从后续操作可推断):
_Ptr:指向管理对象的原始指针(element_type*,即_Ty*)。_Rep:指向控制块的指针(_Ref_count_base*),控制块存储引用计数、删除器等元数据。
_Ref_count_base根据具体传入的参数决定创建各种不同的控制块去实现不同的逻辑,下面具体讲解。
3.2._Ref_count_base实现分析
_Ref_count_base的源码如下:
class __declspec(novtable) _Ref_count_base { // common code for reference counting
private:
#ifdef _M_CEE_PURE// permanent workaround to avoid mentioning _purecall in msvcurt.lib, ptrustu.lib, or other support libsvirtual void _Destroy() noexcept {_CSTD abort();}virtual void _Delete_this() noexcept {_CSTD abort();}
#else // ^^^ defined(_M_CEE_PURE) / !defined(_M_CEE_PURE) vvvvirtual void _Destroy() noexcept = 0; // destroy managed resourcevirtual void _Delete_this() noexcept = 0; // destroy self
#endif // ^^^ !defined(_M_CEE_PURE) ^^^_Atomic_counter_t _Uses = 1;_Atomic_counter_t _Weaks = 1;protected:constexpr _Ref_count_base() noexcept = default; // non-atomic initializationspublic:_Ref_count_base(const _Ref_count_base&) = delete;_Ref_count_base& operator=(const _Ref_count_base&) = delete;virtual ~_Ref_count_base() noexcept {} // TRANSITION, should be non-virtualbool _Incref_nz() noexcept { // increment use count if not zero, return true if successfulauto& _Volatile_uses = reinterpret_cast<volatile long&>(_Uses);
#ifdef _M_CEE_PURElong _Count = *_Atomic_address_as<const long>(&_Volatile_uses);
#elselong _Count = __iso_volatile_load32(reinterpret_cast<volatile int*>(&_Volatile_uses));
#endifwhile (_Count != 0) {const long _Old_value = _INTRIN_RELAXED(_InterlockedCompareExchange)(&_Volatile_uses, _Count + 1, _Count);if (_Old_value == _Count) {return true;}_Count = _Old_value;}return false;}void _Incref() noexcept { // increment use count_MT_INCR(_Uses);}void _Incwref() noexcept { // increment weak reference count_MT_INCR(_Weaks);}void _Decref() noexcept { // decrement use countif (_MT_DECR(_Uses) == 0) {_Destroy();_Decwref();}}void _Decwref() noexcept { // decrement weak reference countif (_MT_DECR(_Weaks) == 0) {_Delete_this();}}long _Use_count() const noexcept {return static_cast<long>(_Uses);}virtual void* _Get_deleter(const type_info&) const noexcept {return nullptr;}
};
_Ref_count_base 中支撑 weak_ptr 的核心机制,在它的类中定义了两个引用计数:
_Atomic_counter_t _Uses = 1; //强引用计数,原子类型确保线程安全
_Atomic_counter_t _Weaks = 1; //弱引用计数,原子类型确保线程安全
- _Uses 记录当前指向该控制块的
shared_ptr数量 _Weaks记录当前指向该控制块的weak_ptr数量(包括观察者持有的weak_ptr)- 初始化值为 1:确保控制块在 “无弱引用但有强引用” 时不被释放,强引用释放后仍能支撑
weak_ptr检测状态。
3.2.1.强引用计数的增减方法
✮_Incref():shared_ptr 构造 / 复制时调用,原子递增 _Uses。在_Ptr_base的 _Copy_construct_from和_Alias_construct_from都有调用:
template <class _Ty2>void _Copy_construct_from(const shared_ptr<_Ty2>& _Other) noexcept {// implement shared_ptr's (converting) copy ctor_Other._Incref();_Ptr = _Other._Ptr;_Rep = _Other._Rep;}template <class _Ty2>void _Alias_construct_from(const shared_ptr<_Ty2>& _Other, element_type* _Px) noexcept {// implement shared_ptr's aliasing ctor_Other._Incref();_Ptr = _Px;_Rep = _Other._Rep;}
✮_Decref(): shared_ptr 析构 / 被std::move时调用,原子递减 _Uses。
✮_Incwref():弱引用计数的递增方法,weak_ptr 构造 / 复制时调用,原子递增 _Weaks(对应观察者注册到被观察者时,持有 weak_ptr)。
void _Incwref() noexcept { _MT_INCR(_Weaks); } // 复用原子递增宏,线程安全
✮_Decwref():弱引用计数的递减方法,weak_ptr 析构时调用,原子递减 _Weaks;若递减后为 0,且强引用 _Uses 已为 0,释放控制块本身。
void _Decwref() noexcept {if (_MT_DECR(_Weaks) == 0) {_Delete_this(); // 最后一个弱引用消失,释放控制块}
}
✮_Incref_nz():观察者安全访问被观察者的关键,当观察者通过 weak_ptr.lock() 尝试访问被观察者时,底层调用 _Incref_nz():
bool _Incref_nz() noexcept { // increment use count if not zero, return true if successfulauto& _Volatile_uses = reinterpret_cast<volatile long&>(_Uses);
#ifdef _M_CEE_PURElong _Count = *_Atomic_address_as<const long>(&_Volatile_uses);
#elselong _Count = __iso_volatile_load32(reinterpret_cast<volatile int*>(&_Volatile_uses));
#endifwhile (_Count != 0) {const long _Old_value = _INTRIN_RELAXED(_InterlockedCompareExchange)(&_Volatile_uses, _Count + 1, _Count);if (_Old_value == _Count) {return true;}_Count = _Old_value;}return false;}
- 功能:仅当强引用
_Uses > 0(被观察者存活)时,原子递增_Uses,返回true(转换为shared_ptr成功)。 - 实现逻辑:
- 原子读取当前
_Uses值(_Volatile_uses确保无编译优化)。 - 循环用 CAS 操作(
_InterlockedCompareExchange)尝试递增_Uses,避免多线程竞态。 - 若
_Uses为 0(被观察者已释放),返回false(转换失败,避免野指针)。
- 原子读取当前
3.2.2.观察者模式中 weak_ptr 与 _Ref_count_base 的联动逻辑
观察者模式的核心矛盾是 “观察者需要跟踪被观察者,但不能延长其生命周期”,_Ref_count_base 的设计完美解决了这一点,具体联动流程如下:
1.被观察者初始化:控制块创建
被观察者通常由 shared_ptr 管理(确保自身生命周期由业务逻辑控制,而非观察者):
// 被观察者类
class Subject {
public:void register_observer(std::weak_ptr<Observer> obs) {observers.push_back(obs); // 存储观察者的 weak_ptr,不增加 _Uses}
private:std::vector<std::weak_ptr<Observer>> observers; // 弱引用集合
};// 业务逻辑创建被观察者(强引用由业务持有)
auto subject = std::make_shared<Subject>(); // 控制块 _Uses=1,_Weaks=1
2.观察者注册:weak_ptr 持有弱引用
观察者通过 register_observer 注册时,被观察者存储其 weak_ptr:
// 观察者类
class Observer {};// 创建观察者并注册
auto observer = std::make_shared<Observer>(); // 观察者自身的控制块
subject->register_observer(observer); // 存储 weak_ptr,触发 _Incwref()
- 关键:
subject->observers存储的是weak_ptr<Observer>,而非shared_ptr。 - 底层:
weak_ptr构造时调用_Incwref(),被观察者的控制块_Weaks递增为 2(初始 1 + 观察者的 weak_ptr 1),但_Uses仍为 1(不影响被观察者生命周期)。
3.被观察者通知观察者:安全检测存活
被观察者状态变化时,遍历 weak_ptr 集合,通过 lock() 转换为 shared_ptr 访问观察者:
void Subject::notify() {for (auto& wp : observers) {if (auto sp = wp.lock()) { // 底层调用 _Incref_nz()// 访问观察者(sp 是 shared_ptr,确保访问期间观察者不被释放)} else {// 观察者已释放,从集合中移除(清理无效引用)}}
}
- 底层逻辑:
wp.lock()调用_Incref_nz(),检查观察者的控制块_Uses是否 > 0(观察者存活)。- 若存活:
_Uses递增,返回shared_ptr,确保访问期间观察者不被释放(避免野指针)。 - 若已释放:返回空
shared_ptr,跳过无效观察者(避免访问已释放内存)。
4.被观察者释放:不被观察者 “卡住”
当业务逻辑不再需要被观察者时,释放 shared_ptr:
subject.reset(); // 被观察者的控制块 _Uses 递减为 0
- 底层触发
_Decref():_MT_DECR(_Uses)后_Uses=0,调用_Destroy()释放被观察者对象。- 调用
_Decwref(),_Weaks递减为 1(观察者的 weak_ptr 仍持有)。 - 此时
_Weaks不为 0,控制块不释放(仍支撑观察者检测状态)。
5.观察者释放:控制块最终释放
当观察者也被释放时:
observer.reset(); // 观察者的 weak_ptr 析构,触发 _Decwref()
- 底层:
weak_ptr析构时调用_Decwref(),被观察者的控制块_Weaks递减为 0。 - 此时
_Uses已为 0,_Decwref()调用_Delete_this(),释放控制块本身(无内存泄漏)。
3.2.3.核心优势:_Ref_count_base 如何解决观察者模式的痛点
- 不延长被观察者生命周期:观察者持有
weak_ptr仅操作_Weaks计数,不影响_Uses,被观察者的生命周期由业务逻辑的shared_ptr决定(_Uses为 0 即释放)。 - 避免野指针:
lock()依赖_Incref_nz()检测_Uses,确保仅在被观察者存活时才访问,杜绝野指针访问。 - 线程安全:
_Uses和_Weaks均为原子类型,增减操作通过_MT_INCR/_MT_DECR(原子函数)实现,支持多线程下的注册、通知、释放。 - 无内存泄漏:控制块仅在
_Uses和_Weaks均为 0 时才释放,既支撑weak_ptr跟踪状态,又避免控制块泄漏。
3.3._Ref_count实现分析
_Ref_count<_Ty> 是 _Ref_count_base 的派生类,专门用于 无自定义删除器 的场景 —— 当 shared_ptr 管理的资源需通过默认 delete 释放时(如单个对象 new T),会创建此类的控制块,核心是实现 “默认资源释放” 和 “控制块自销毁” 的具体逻辑。
template <class _Ty>
class _Ref_count : public _Ref_count_base { // handle reference counting for pointer without deleter
public:explicit _Ref_count(_Ty* _Px) : _Ref_count_base(), _Ptr(_Px) {}private:void _Destroy() noexcept override { // destroy managed resourcedelete _Ptr;}void _Delete_this() noexcept override { // destroy selfdelete this;}_Ty* _Ptr;
};
_Ref_count_base 中的 _Destroy(释放资源)和 _Delete_this(释放控制块)是纯虚函数,_Ref_count 需提供具体实现 —— 核心是 “用默认 delete 释放对象,用 delete 释放控制块自身”。
关键约束与注意事项
1.仅支持单个对象释放:_Destroy 中用 delete _Ptr,仅适用于 new T 分配的单个对象,不适用于数组(数组需 delete[],对应 _Can_array_delete 约束)。
2.依赖父类原子操作:所有计数增减(_Uses/_Weaks)均由父类 _Ref_count_base 提供的原子函数(_MT_INCR/_MT_DECR)实现,_Ref_count 仅专注释放逻辑。
3.禁止手动调用虚函数:_Destroy 和 _Delete_this 是父类触发的内部接口,用户无需手动调用,否则会导致重复释放(未定义行为)。
总结:
_Ref_count<_Ty> 是 shared_ptr 控制块的 “默认释放版本”,核心价值是:
- 继承父类的原子计数能力,无需重复实现线程安全逻辑。
- 封装默认释放策略(
delete _Ptr),适配无自定义删除器的常规场景。 - 通过重写
_Destroy和_Delete_this,完成 “对象释放” 和 “控制块释放” 的闭环,确保无内存泄漏。
3.4._Ref_count_resource实现分析
_Ref_count_resource<_Resource, _Dx> 是 _Ref_count_base 的派生类,专门用于 带自定义删除器 的场景 —— 当 shared_ptr 管理的资源需要通过非默认方式释放(如文件句柄、网络连接、数组等,不能用 delete 直接释放)时,会创建此类的控制块,核心是封装 “自定义删除器” 和 “资源”,实现灵活的释放逻辑。
template <class _Resource, class _Dx>
class _Ref_count_resource : public _Ref_count_base { // handle reference counting for object with deleter
public:_Ref_count_resource(_Resource _Px, _Dx _Dt): _Ref_count_base(), _Mypair(_One_then_variadic_args_t{}, _STD move(_Dt), _Px) {}~_Ref_count_resource() noexcept override = default; // TRANSITION, should be non-virtualvoid* _Get_deleter(const type_info& _Typeid) const noexcept override {
#if _HAS_STATIC_RTTIif (_Typeid == typeid(_Dx)) {return const_cast<_Dx*>(_STD addressof(_Mypair._Get_first()));}
#else // ^^^ _HAS_STATIC_RTTI / !_HAS_STATIC_RTTI vvv(void) _Typeid;
#endif // ^^^ !_HAS_STATIC_RTTI ^^^return nullptr;}private:void _Destroy() noexcept override { // destroy managed resource_Mypair._Get_first()(_Mypair._Myval2);}void _Delete_this() noexcept override { // destroy selfdelete this;}_Compressed_pair<_Dx, _Resource> _Mypair;
};
3.4.1.核心定位与适用场景
- 父类:
_Ref_count_base(继承原子引用计数、_Decref/_Decwref等通用逻辑)。 - 核心职责:存储自定义删除器(
_Dx)和资源(_Resource),在资源需释放时调用删除器,替代默认的delete。 - 适用场景:
shared_ptr构造时传入自定义删除器的场景,例如:- 管理文件指针(用
fclose释放); - 管理动态数组(C++17 前用
delete[]作为自定义删除器); - 管理第三方库资源(用库提供的释放函数)。
- 管理文件指针(用
3.4.2.关键成员与构造函数
1.核心成员:_Compressed_pair<_Dx, _Resource> _Mypair
_Compressed_pair是什么:MSVC 标准库的优化版pair,核心优势是 “空基类优化(EBO)”—— 若删除器_Dx是 “空类型”(如无状态 lambda、空函数对象),则不会占用额外内存,比普通std::pair更节省空间。- 存储内容:
- 第一个元素(
_Get_first()):自定义删除器_Dx(通过std::move接收,避免拷贝开销); - 第二个元素(
_Myval2):资源_Resource(可以是指针、句柄等,如FILE*、int*、网络连接句柄)。
- 第一个元素(
2.构造函数:绑定资源与删除器
_Ref_count_resource(_Resource _Px, _Dx _Dt): _Ref_count_base(), _Mypair(_One_then_variadic_args_t{}, _STD move(_Dt), _Px) {}
- 初始化父类
_Ref_count_base:_Uses=1(第一个shared_ptr持有)、_Weaks=1(控制块初始引用)。 - 绑定资源与删除器:
_Px:待管理的资源(如fopen返回的FILE*);_Dt:自定义删除器(如fclose函数、lambda 表达式),通过std::move转移所有权,提升效率;_Mypair初始化:按 “删除器在前、资源在后” 的顺序存储,利用_Compressed_pair的内存优化。
3.4.3.核心重写函数:释放逻辑的实现
1. _Destroy():调用删除器释放资源
2. _Delete_this():释放控制块自身
3. _Get_deleter():获取自定义删除器
3.4.4.关键优势与约束
1.核心优势
- 支持任意自定义释放逻辑:删除器可以是函数指针、lambda、函数对象等,适配文件、网络连接、数组等非默认资源。
- 内存优化:
_Compressed_pair避免空删除器占用额外内存,比普通pair更高效。 - 线程安全:复用父类的原子计数操作,多线程下复制、销毁
shared_ptr/weak_ptr安全。
2.重要约束
- 删除器必须可调用且参数匹配:删除器
_Dx必须能接收_Resource类型的参数(如Dx(Resource)合法),否则编译报错(由之前的_Can_call_function_object模板检查)。 - 资源类型需匹配删除器:若资源是
FILE*,删除器必须能处理FILE*(如fclose),否则会导致未定义行为。 - 控制块自身用
delete释放:控制块是new分配的,因此_Delete_this用delete this,若控制块通过自定义分配器创建,需用其他派生类(如_Ref_count_resource_alloc)。
3.5._Ref_count_resource_alloc实现分析
_Ref_count_resource_alloc<_Resource, _Dx, _Alloc> 是 _Ref_count_base 的派生类,专门用于 同时带有自定义删除器和自定义分配器 的场景 —— 当 shared_ptr 不仅需要自定义逻辑释放资源(删除器),还需要用自定义分配器管理控制块自身的内存时(如 std::allocate_shared 函数),会创建此类控制块。其核心是将 “资源释放”“控制块内存管理” 完全交给用户指定的删除器和分配器,实现更灵活的内存控制。
3.5.1.核心定位与适用场景
- 父类:
_Ref_count_base(继承原子引用计数、_Decref/_Decwref等通用逻辑)。 - 核心职责:
- 存储自定义删除器(
_Dx)、资源(_Resource)和自定义分配器(_Alloc)。 - 通过删除器释放资源,通过分配器释放控制块自身内存(替代默认
delete)。
- 存储自定义删除器(
- 适用场景:需用自定义分配器管理控制块内存的场景,最典型的是
std::allocate_shared(用指定分配器一次性分配资源和控制块内存,提升效率)。
3.5.2.关键成员与构造函数
1.核心成员:嵌套的 _Compressed_pair 存储三要素
_Compressed_pair<_Dx, _Compressed_pair<_Myalty, _Resource>> _Mypair;
_Compressed_pair 的嵌套设计:
外层存储 “删除器 _Dx” 和 “内层 _Compressed_pair”;内层存储 “分配器 _Myalty” 和 “资源 _Resource”。这种嵌套利用 _Compressed_pair 的 “空基类优化(EBO)”,对空删除器或空分配器(如 std::allocator)不占用额外内存,比普通结构体更节省空间。
_Myalty 类型:
using _Myalty = _Rebind_alloc_t<_Alloc, _Ref_count_resource_alloc>;
_Rebind_alloc_t 是分配器的 “重绑定” 工具,将原始分配器 _Alloc 适配为能分配 _Ref_count_resource_alloc 类型内存的分配器(因为分配器需要为控制块自身分配内存,类型必须匹配控制块类型)。
2.构造函数:绑定资源、删除器和分配器
_Ref_count_resource_alloc(_Resource _Px, _Dx _Dt, const _Alloc& _Ax): _Ref_count_base(),_Mypair(_One_then_variadic_args_t{}, _STD move(_Dt), _One_then_variadic_args_t{}, _Ax, _Px) {}
- 初始化父类
_Ref_count_base:_Uses=1(第一个shared_ptr持有)、_Weaks=1(控制块初始引用)。 - 绑定三要素:
_Dt(删除器):通过std::move转移所有权,存储到外层_Compressed_pair的第一个元素;_Ax(分配器):作为内层_Compressed_pair的第一个元素,存储重绑定后的_Myalty实例;_Px(资源):作为内层_Compressed_pair的第二个元素,存储待管理的资源(如对象指针、句柄等)。
3.5.3.核心重写函数:释放逻辑的实现
_Ref_count_resource_alloc 重写父类的 _Destroy(释放资源)、_Delete_this(释放控制块)和 _Get_deleter(获取删除器),核心差异在于 控制块的释放依赖自定义分配器。
1._Destroy():调用删除器释放资源
void _Destroy() noexcept override {_Mypair._Get_first()(_Mypair._Myval2._Myval2); // 删除器(资源):执行自定义释放
}
_Mypair._Get_first():获取外层存储的删除器_Dx;_Mypair._Myval2._Myval2:获取内层存储的资源_Resource(嵌套访问路径);- 执行
删除器(资源):通过自定义删除器释放资源(与_Ref_count_resource逻辑一致)。
2._Delete_this():用分配器释放控制块自身
这是与 _Ref_count_resource 的核心区别 —— 控制块内存由自定义分配器分配,因此必须用同一分配器释放:
void _Delete_this() noexcept override {_Myalty _Al = _Mypair._Myval2._Get_first(); // 获取内层存储的分配器this->~_Ref_count_resource_alloc(); // 显式调用控制块析构函数(销毁成员)_STD _Deallocate_plain(_Al, this); // 用分配器释放控制块内存
}
- 获取分配器:从内层
_Compressed_pair中取出_Myalty分配器实例_Al; - 显式析构:调用
this->~_Ref_count_resource_alloc(),销毁控制块的成员(删除器、分配器、资源指针等); - 释放内存:通过
_Deallocate_plain(分配器的释放工具),用_Al释放控制块占用的内存(替代默认delete)。
3._Get_deleter():获取自定义删除器
void* _Get_deleter(const type_info& _Typeid) const noexcept override {
#if _HAS_STATIC_RTTIif (_Typeid == typeid(_Dx)) {return const_cast<_Dx*>(_STD addressof(_Mypair._Get_first()));}
#else(void) _Typeid;
#endifreturn nullptr;
}
- 功能与
_Ref_count_resource一致:支持std::get_deleter函数,通过类型匹配返回删除器地址(仅在开启 RTTI 时有效)。
3.5.4.核心优势与约束
1.核心优势
- 统一内存管理:通过自定义分配器同时管理资源和控制块的内存(如
allocate_shared一次性分配),减少内存碎片,提升性能。 - 适配分配器需求:支持需要特殊内存管理的场景(如共享内存、内存池),控制块的创建 / 释放完全遵循用户指定的分配器策略。
- 兼容删除器:同时支持自定义删除器,兼顾资源释放灵活性和内存管理灵活性。
2.重要约束
- 分配器必须可重绑定:
_Alloc需支持重绑定为_Myalty(大多数标准分配器都支持),否则编译报错。 - 释放与分配匹配:控制块由分配器
_Al分配,必须由同一分配器释放(_Delete_this中严格遵守),否则导致未定义行为。 - 删除器参数匹配:删除器
_Dx必须能接收_Resource类型参数(由_Can_call_function_object模板检查)。
3.6._Ref_count_obj2实现分析
_Ref_count_obj2<_Ty> 是 _Ref_count_base 的派生类,专门用于 “对象与控制块存储在同一块内存” 的场景 —— 当 shared_ptr 管理的对象被直接嵌入控制块内部(而非单独分配内存)时,会使用此类控制块。这是 std::make_shared 等函数的底层实现核心,通过 “一次性分配控制块 + 对象内存” 减少内存碎片,提升性能。
template <class _Ty>
class _Ref_count_obj2 : public _Ref_count_base { // handle reference counting for object in control block, no allocator
public:template <class... _Types>explicit _Ref_count_obj2(_Types&&... _Args) : _Ref_count_base() {
#if _HAS_CXX20if constexpr (sizeof...(_Types) == 1 && (is_same_v<_For_overwrite_tag, remove_cvref_t<_Types>> && ...)) {_STD _Default_construct_in_place(_Storage._Value);((void) _Args, ...);} else
#endif // _HAS_CXX20{_STD _Construct_in_place(_Storage._Value, _STD forward<_Types>(_Args)...);}}~_Ref_count_obj2() noexcept override { // TRANSITION, should be non-virtual// nothing to do, _Storage._Value was already destroyed in _Destroy// N4950 [class.dtor]/7:// "A defaulted destructor for a class X is defined as deleted if:// X is a union-like class that has a variant member with a non-trivial destructor"}union {_Wrap<remove_cv_t<_Ty>> _Storage;};private:void _Destroy() noexcept override { // destroy managed resource_STD _Destroy_in_place(_Storage._Value);}void _Delete_this() noexcept override { // destroy selfdelete this;}
};
3.6.1.核心定位与适用场景
- 父类:
_Ref_count_base(继承原子引用计数、_Decref/_Decwref等通用逻辑)。 - 核心特征:管理的对象(
_Ty类型)直接存储在控制块内部,而非通过指针指向外部内存。 - 适用场景:
std::make_shared或std::allocate_shared(无自定义分配器时)创建shared_ptr时 —— 这类函数会一次性分配一块内存,同时容纳控制块和对象,避免单独为对象和控制块分配内存(减少一次内存分配和指针解引用)。
3.6.2.关键成员:嵌入对象的存储 union
union {_Wrap<remove_cv_t<_Ty>> _Storage;
};
_Wrap作用:包装_Ty类型的对象,确保对象能在控制块的内存空间中 “原地构造” 和 “原地销毁”(不额外分配内存)。remove_cv_t<_Ty>用于移除_Ty的const/volatile修饰,统一存储类型。union用途:通过 union 确保_Storage占据的内存仅用于存储_Ty对象,不引入额外的成员变量开销(内存优化)。
3.6.3.构造函数:原地构造对象(核心逻辑)
构造函数通过可变参数模板,在控制块内部直接构造 _Ty 类型的对象(避免单独 new 分配):
template <class... _Types>
explicit _Ref_count_obj2(_Types&&... _Args) : _Ref_count_base() {
#if _HAS_CXX20// 处理 C++20 的 _For_overwrite_tag 场景(用于覆盖构造,避免初始化)if constexpr (sizeof...(_Types) == 1 && (is_same_v<_For_overwrite_tag, remove_cvref_t<_Types>> && ...)) {_STD _Default_construct_in_place(_Storage._Value); // 默认构造(覆盖内存)((void) _Args, ...); // 忽略标签参数} else
#endif // _HAS_CXX20{// 常规场景:用参数在原地构造对象_STD _Construct_in_place(_Storage._Value, _STD forward<_Types>(_Args)...);}
}
- 核心动作:调用
_Construct_in_place(原地构造工具),在_Storage._Value指向的内存位置直接构造_Ty对象,参数通过完美转发(forward)传递给_Ty的构造函数。 - C++20 扩展:支持
_For_overwrite_tag标签(如std::make_shared_for_overwrite),此时调用_Default_construct_in_place进行默认构造(适用于需要覆盖已有内存的场景,避免不必要的初始化开销)。
3.6.4.重写函数:释放逻辑适配 “嵌入对象”
1._Destroy():原地销毁对象
void _Destroy() noexcept override {_STD _Destroy_in_place(_Storage._Value); // 原地销毁嵌入的 _Ty 对象
}
- 调用时机:父类
_Decref()检测到_Uses递减为 0 时(最后一个shared_ptr销毁)。 - 逻辑:通过
_Destroy_in_place直接调用_Ty的析构函数,销毁控制块内部存储的对象(无需delete外部指针,因为对象与控制块在同一块内存)。
2._Delete_this():释放控制块(含对象内存)
3.析构函数:空实现
~_Ref_count_obj2() noexcept override {// 无需操作,对象已在 _Destroy 中销毁
}
- 原因:
_Ty对象的析构由_Destroy负责(在_Uses归零时调用),控制块的析构函数无需重复操作,避免二次销毁。
3.6.5.核心优势与约束
1.核心优势
- 性能优化:
make_shared通过_Ref_count_obj2一次性分配控制块和对象内存,比 “new对象 + 单独控制块” 减少一次内存分配(和对应的内存对齐开销),且对象与控制块在同一块内存,缓存 locality 更好。 - 内存紧凑:对象嵌入控制块,避免额外的指针成员(如
_Ref_count中的_Ptr),节省内存。 - 适配原地构造:支持在控制块内部直接构造对象,无需外部指针,简化内存管理。
2.重要约束
- 对象类型限制:
_Ty必须是可在控制块内部构造的类型(非抽象类、有匹配的构造函数),否则编译报错。 - 内存释放绑定:对象内存与控制块内存绑定,只有当控制块被释放时(
_Weaks归零时),整块内存才会释放。这意味着即使_Uses归零时对象已销毁,内存也可能因weak_ptr存在而延迟释放(这是make_shared的 trade-off)。 - 无自定义分配器:此类不支持自定义分配器(注释明确 “no allocator”),需用
_Ref_count_resource_alloc处理带分配器的场景。
_Ref_count_obj2<_Ty> 是 shared_ptr 实现中 “对象与控制块一体化存储” 的核心控制块,专为 std::make_shared 设计:
- 通过 “原地构造 / 销毁” 将对象嵌入控制块,减少内存分配次数,提升性能。
- 复用
_Ref_count_base的原子计数逻辑,确保线程安全。 - 是理解
make_shared高效性的关键 —— 其性能优势正源于此控制块的 “一体化内存管理” 设计。
3.7._Ref_count_obj_alloc3实现分析
_Ref_count_obj_alloc3<_Ty, _Alloc> 是 _Ref_count_base 的派生类,核心定位是 “对象嵌入控制块 + 自定义分配器管理内存”—— 它将管理的对象直接存储在控制块内部(类似 _Ref_count_obj2),同时通过自定义分配器管理控制块(含嵌入对象)的内存(类似 _Ref_count_resource_alloc)。这是 std::allocate_shared 函数的底层实现核心,兼顾了 “一体化内存分配” 的性能优势和 “自定义分配器” 的灵活内存管理。
3.7.1.核心定位与适用场景
- 父类组合:
_Ref_count_base:继承原子引用计数、_Decref/_Decwref等通用逻辑。_Ebco_base<_Rebind_alloc_t<_Alloc, _Ty>>:_Ebco_base是 “空基类优化(EBO)” 基类,用于存储重绑定后的分配器,避免空分配器占用额外内存。
- 核心特征:
- 对象嵌入控制块:
_Ty类型对象直接存储在控制块内部,不单独分配内存。 - 分配器管理内存:控制块(含嵌入对象)的内存由用户指定的自定义分配器分配 / 释放。
- 对象嵌入控制块:
- 适用场景:
std::allocate_shared函数创建shared_ptr时 —— 该函数用自定义分配器一次性分配 “控制块 + 对象” 的内存,既减少内存碎片,又支持定制化内存管理(如内存池、共享内存)。
3.7.2.关键成员与继承逻辑
1.类型别名与静态断言
static_assert(is_same_v<_Ty, remove_cv_t<_Ty>>, "allocate_shared should remove_cv_t");
using _Rebound = _Rebind_alloc_t<_Alloc, _Ty>;
- 静态断言:确保
_Ty已移除const/volatile修饰(allocate_shared内部会自动处理,避免 cv 修饰导致的存储冲突)。 _Rebound:将原始分配器_Alloc重绑定为能构造_Ty类型对象的分配器(适配对象的构造 / 销毁需求)。
2.嵌入对象的存储 union
union {_Wrap<_Ty> _Storage;
};
- 与
_Ref_count_obj2逻辑一致:_Wrap<_Ty>包装_Ty对象,确保其能在控制块内存中 “原地构造 / 销毁”;union 优化内存,避免额外成员开销。 _Storage._Value是_Ty对象的实际存储位置,与控制块共用同一块内存。
3.7.3.构造函数:分配器 + 原地构造对象
构造函数的核心是 “用分配器初始化控制块内存,在控制块内原地构造对象”:
template <class... _Types>
explicit _Ref_count_obj_alloc3(const _Alloc& _Al_arg, _Types&&... _Args): _Ebco_base<_Rebound>(_Al_arg), _Ref_count_base() {
#if _HAS_CXX20 && defined(_ENABLE_STL_INTERNAL_CHECK)// 断言:禁止用 _For_overwrite_tag(该标签适配其他控制块类型)if constexpr (sizeof...(_Types) == 1) {_STL_INTERNAL_STATIC_ASSERT(!(is_same_v<_For_overwrite_tag, remove_cvref_t<_Types>> && ...));}
#endif// 用分配器原地构造 _Ty 对象allocator_traits<_Rebound>::construct(this->_Get_val(), _STD addressof(_Storage._Value), _STD forward<_Types>(_Args)...);
}
- 初始化父类:
_Ebco_base<_Rebound>(_Al_arg):将自定义分配器_Al_arg存储到基类(EBO 优化,无额外内存开销)。_Ref_count_base():初始化引用计数(_Uses=1、_Weaks=1)。
- 原地构造对象:
- 调用
allocator_traits<_Rebound>::construct(分配器的构造工具),在_Storage._Value位置直接构造_Ty对象。 - 通过完美转发(
forward<_Types>(_Args)...)将参数传递给_Ty的构造函数,确保参数传递无冗余拷贝。
- 调用
3.7.4.重写函数:释放逻辑适配 “分配器 + 嵌入对象”
1. _Destroy():用分配器销毁嵌入对象
void _Destroy() noexcept override {// 调用分配器的 destroy 方法,原地销毁 _Ty 对象allocator_traits<_Rebound>::destroy(this->_Get_val(), _STD addressof(_Storage._Value));
}
- 调用时机:父类
_Decref()检测到_Uses递减为 0 时(最后一个shared_ptr销毁)。 - 核心逻辑:通过分配器的
destroy方法调用_Ty的析构函数,销毁控制块内的嵌入对象(与构造时的construct方法配对)。
2._Delete_this():用分配器释放控制块内存
void _Delete_this() noexcept override {// 重绑定分配器:适配控制块自身的类型(_Ref_count_obj_alloc3)_Rebind_alloc_t<_Alloc, _Ref_count_obj_alloc3> _Al(this->_Get_val());this->~_Ref_count_obj_alloc3(); // 显式析构控制块(销毁成员)_STD _Deallocate_plain(_Al, this); // 用分配器释放控制块内存
}
- 核心差异:控制块内存由自定义分配器分配,因此释放时需遵循 “分配 - 释放匹配” 原则:
- 重绑定分配器:将原始分配器
_Alloc适配为能释放_Ref_count_obj_alloc3类型的分配器_Al。 - 显式析构:调用控制块的析构函数,销毁内部成员(如分配器、
_Storage)。 - 释放内存:通过
_Deallocate_plain用分配器_Al释放整块内存(含控制块和已销毁的对象)。
- 重绑定分配器:将原始分配器
3.析构函数:空实现
~_Ref_count_obj_alloc3() noexcept override {// 无需操作:对象已在 _Destroy() 中通过分配器销毁
}
- 原因:
_Ty对象的析构由分配器的destroy方法完成,控制块析构函数无需重复处理,避免二次销毁。
3.7.5.应用场景
内存分配与控制块 + 对象构造
// 自定义分配器(如内存池分配器)
MyAllocator<MyClass> alloc;
// allocate_shared 用 alloc 一次性分配控制块+对象内存
auto sp = std::allocate_shared<MyClass>(alloc, 42); // MyClass 构造参数为 42
- 底层动作:
- 分配器
alloc被重绑定为_Rebound(适配MyClass构造)和_Rebind_alloc_t<_Alloc, _Ref_count_obj_alloc3>(适配控制块分配)。 - 用重绑定后的分配器分配一块连续内存,大小 = 控制块大小 +
MyClass对象大小。 - 在该内存中构造
_Ref_count_obj_alloc3控制块,调用其构造函数:- 存储分配器
alloc到_Ebco_base。 - 用
allocator_traits<_Rebound>::construct在_Storage._Value位置构造MyClass(42)。
- 存储分配器
- 分配器
3.7.6.核心优势与约束
1.核心优势
- 性能 + 灵活双优:既像
_Ref_count_obj2那样 “一次性分配控制块 + 对象”(减少内存碎片、提升缓存 locality),又支持自定义分配器(适配内存池、共享内存等场景)。 - 内存优化:
_Ebco_base实现分配器的空基类优化,union实现对象的紧凑存储,无冗余内存开销。 - 分配 - 释放匹配:严格通过自定义分配器管理控制块内存,避免默认
delete与自定义分配器的不匹配问题。
2.重要约束
- 分配器需支持重绑定:
_Alloc必须能被_Rebind_alloc_t重绑定为适配_Ty和控制块的类型(标准分配器均支持)。 - 对象嵌入限制:
_Ty需支持 “原地构造 / 销毁”(非抽象类、有匹配的构造函数),且不能是数组类型(数组需单独处理)。 - 无自定义删除器:对象的销毁依赖分配器的
destroy方法(本质是调用_Ty的析构函数),不支持额外自定义删除器(需自定义删除器时用_Ref_count_resource_alloc)。
总结
_Ref_count_obj_alloc3<_Ty, _Alloc> 是 shared_ptr 控制块中 “一体化存储 + 定制化内存” 的核心实现,核心价值是:
- 结合 “对象嵌入控制块”(减少内存分配)和 “自定义分配器”(灵活内存管理),适配
allocate_shared的高性能 + 定制化需求。 - 通过分配器的
construct/destroy管理对象生命周期,通过重绑定分配器管理控制块内存,确保分配 - 释放匹配。 - 是
std::allocate_shared函数的底层支撑,让智能指针在需要定制内存管理(如内存池)时,仍能保持make_shared级别的性能优势。
3.7.7.与其他控制块类的核心区别
| 控制块类 | 对象存储方式 | 分配器支持 | 自定义删除器支持 | 核心场景 |
|---|---|---|---|---|
_Ref_count | 外部指针 | 否 | 否 | 无删除器、默认分配器 |
_Ref_count_resource | 外部指针 | 否 | 是 | 有删除器、默认分配器 |
_Ref_count_resource_alloc | 外部指针 | 是 | 是 | 有删除器、自定义分配器 |
_Ref_count_obj2 | 嵌入控制块 | 否 | 否 | 无删除器、make_shared |
_Ref_count_obj_alloc3 | 嵌入控制块 | 是 | 否 | 无删除器、allocate_shared |
3.8.std::shared_ptr实现分析
3.8.1.继承关系与核心成员
template <class _Ty>
class _Ptr_base { // base class for shared_ptr and weak_ptr
public:using element_type = remove_extent_t<_Ty>;_NODISCARD long use_count() const noexcept {return _Rep ? _Rep->_Use_count() : 0;}template <class _Ty2>_NODISCARD bool owner_before(const _Ptr_base<_Ty2>& _Right) const noexcept { // compare addresses of manager objectsreturn _Rep < _Right._Rep;}_Ptr_base(const _Ptr_base&) = delete;_Ptr_base& operator=(const _Ptr_base&) = delete;protected:_NODISCARD element_type* get() const noexcept {return _Ptr;}constexpr _Ptr_base() noexcept = default;~_Ptr_base() = default;template <class _Ty2>void _Move_construct_from(_Ptr_base<_Ty2>&& _Right) noexcept {// implement shared_ptr's (converting) move ctor and weak_ptr's move ctor_Ptr = _Right._Ptr;_Rep = _Right._Rep;_Right._Ptr = nullptr;_Right._Rep = nullptr;}template <class _Ty2>void _Copy_construct_from(const shared_ptr<_Ty2>& _Other) noexcept {// implement shared_ptr's (converting) copy ctor_Other._Incref();_Ptr = _Other._Ptr;_Rep = _Other._Rep;}template <class _Ty2>void _Alias_construct_from(const shared_ptr<_Ty2>& _Other, element_type* _Px) noexcept {// implement shared_ptr's aliasing ctor_Other._Incref();_Ptr = _Px;_Rep = _Other._Rep;}template <class _Ty2>void _Alias_move_construct_from(shared_ptr<_Ty2>&& _Other, element_type* _Px) noexcept {// implement shared_ptr's aliasing move ctor_Ptr = _Px;_Rep = _Other._Rep;_Other._Ptr = nullptr;_Other._Rep = nullptr;}template <class _Ty0>friend class weak_ptr; // specifically, weak_ptr::lock()template <class _Ty2>bool _Construct_from_weak(const weak_ptr<_Ty2>& _Other) noexcept {// implement shared_ptr's ctor from weak_ptr, and weak_ptr::lock()if (_Other._Rep && _Other._Rep->_Incref_nz()) {_Ptr = _Other._Ptr;_Rep = _Other._Rep;return true;}return false;}void _Incref() const noexcept {if (_Rep) {_Rep->_Incref();}}void _Decref() noexcept { // decrement reference countif (_Rep) {_Rep->_Decref();}}void _Swap(_Ptr_base& _Right) noexcept { // swap pointers_STD swap(_Ptr, _Right._Ptr);_STD swap(_Rep, _Right._Rep);}template <class _Ty2>void _Weakly_construct_from(const _Ptr_base<_Ty2>& _Other) noexcept { // implement weak_ptr's ctorsif (_Other._Rep) {_Ptr = _Other._Ptr;_Rep = _Other._Rep;_Rep->_Incwref();} else {_STL_INTERNAL_CHECK(!_Ptr && !_Rep);}}template <class _Ty2>void _Weakly_convert_lvalue_avoiding_expired_conversions(const _Ptr_base<_Ty2>& _Other) noexcept {// implement weak_ptr's copy converting ctorif (_Other._Rep) {_Rep = _Other._Rep; // always share ownership_Rep->_Incwref();if (_Rep->_Incref_nz()) {_Ptr = _Other._Ptr; // keep resource alive during conversion, handling virtual inheritance_Rep->_Decref();} else {_STL_INTERNAL_CHECK(!_Ptr);}} else {_STL_INTERNAL_CHECK(!_Ptr && !_Rep);}}template <class _Ty2>void _Weakly_convert_rvalue_avoiding_expired_conversions(_Ptr_base<_Ty2>&& _Other) noexcept {// implement weak_ptr's move converting ctor_Rep = _Other._Rep; // always transfer ownership_Other._Rep = nullptr;if (_Rep && _Rep->_Incref_nz()) {_Ptr = _Other._Ptr; // keep resource alive during conversion, handling virtual inheritance_Rep->_Decref();} else {_STL_INTERNAL_CHECK(!_Ptr);}_Other._Ptr = nullptr;}void _Incwref() const noexcept {if (_Rep) {_Rep->_Incwref();}}void _Decwref() noexcept { // decrement weak reference countif (_Rep) {_Rep->_Decwref();}}private:element_type* _Ptr{nullptr};_Ref_count_base* _Rep{nullptr};//。。。
};
shared_ptr继承自_Ptr_base<_Ty>(代码中using _Mybase = _Ptr_base<_Ty>),_Ptr_base是智能指针的基础类,通常包含两个核心成员(从后续操作可推断):
_Ptr:指向管理对象的原始指针(element_type*,即_Ty*)。_Rep:指向控制块的指针(_Ref_count_base*),控制块存储引用计数、删除器等元数据。
3.8.2.构造函数:控制块的创建与初始化
shared_ptr的构造函数是创建控制块、关联资源的核心入口,不同构造场景对应不同的控制块初始化逻辑:
1.原始指针构造(explicit shared_ptr(_Ux* _Px))
template <class _Ux,enable_if_t<conjunction_v<conditional_t<is_array_v<_Ty>, _Can_array_delete<_Ux>, _Can_scalar_delete<_Ux>>,_SP_convertible<_Ux, _Ty>>,int> = 0>
explicit shared_ptr(_Ux* _Px) { // construct shared_ptr object that owns _Pxif constexpr (is_array_v<_Ty>) {_Setpd(_Px, default_delete<_Ux[]>{});} else {_Temporary_owner<_Ux> _Owner(_Px);_Set_ptr_rep_and_enable_shared(_Owner._Ptr, new _Ref_count<_Ux>(_Owner._Ptr));_Owner._Ptr = nullptr;}
}
当用原始指针(如new int(42))构造shared_ptr时,代码会:
-
对非数组类型:通过
new _Ref_count<_Ux>(_Owner._Ptr)创建控制块(_Ref_count是_Ref_count_base的派生类,存储引用计数和默认删除器)。 -
调用
_Set_ptr_rep_and_enable_shared关联_Ptr(对象指针)和_Rep(控制块指针),同时处理enable_shared_from_this逻辑(若对象继承自enable_shared_from_this,会初始化其内部的weak_ptr)。这里的
_Temporary_owner<_Ux>是临时所有权管理工具,确保若控制块创建失败,原始指针能被正确释放(避免内存泄漏)。
C++编译期间验证单个对象可以被释放、验证数组可以被释放和验证函数对象能否被指定类型参数调用
2.带自定义删除器的构造(shared_ptr(_Ux* _Px, _Dx _Dt))
template <class _Ux, class _Dx,enable_if_t<conjunction_v<is_move_constructible<_Dx>, _Can_call_function_object<_Dx&, _Ux*&>,_SP_convertible<_Ux, _Ty>>,int> = 0>
shared_ptr(_Ux* _Px, _Dx _Dt) { // construct with _Px, deleter_Setpd(_Px, _STD move(_Dt));
}
当传入自定义删除器(如释放文件句柄的 lambda)时,代码通过_Setpd函数:
- 创建
_Ref_count_resource<_UxptrOrNullptr, _Dx>类型的控制块(存储自定义删除器)。 - 同样通过
_Set_ptr_rep_and_enable_shared关联指针和控制块,_Temporary_owner_del确保构造失败时删除器能正确执行。
3.带自定义分配器和自定义删除器的构造
template <class _Ux, class _Dx, class _Alloc,enable_if_t<conjunction_v<is_move_constructible<_Dx>, _Can_call_function_object<_Dx&, _Ux*&>,_SP_convertible<_Ux, _Ty>>,int> = 0>
shared_ptr(_Ux* _Px, _Dx _Dt, _Alloc _Ax) { // construct with _Px, deleter, allocator_Setpda(_Px, _STD move(_Dt), _Ax);
}
这个 shared_ptr 构造函数是 “资源指针 + 自定义删除器 + 自定义分配器” 的专用初始化版本,核心作用是:让 shared_ptr 同时管理 “指定资源(_Ux*)”“自定义释放逻辑(_Dx)” 和 “控制块内存分配策略(_Alloc)”,且通过模板约束(SFINAE)确保参数合法性,避免编译错误。
_Ux:资源指针的原始类型(如FILE*、int*),即_Px的类型。_Dx:自定义删除器类型(如 lambda、函数对象、函数指针)。_Alloc:自定义分配器类型(用于分配shared_ptr内部控制块的内存)。- 第四个参数:
enable_if_t<...>是模板约束,通过 SFINAE 过滤无效参数组合。
如:
#include <memory>
#include <cstdio>
#include <iostream>// 自定义内存池分配器(简化版,实际可替换为真实内存池)
template <class T>
struct MyPoolAllocator {using value_type = T;T* allocate(size_t n) { return static_cast<T*>(operator new(n * sizeof(T))); }void deallocate(T* p, size_t) { operator delete(p); }
};int main() {// 1. 自定义删除器:关闭文件auto file_deleter = [](FILE* fp) {if (fp) {std::fclose(fp);std::cout << "文件已关闭" << std::endl;}};// 2. 自定义分配器(内存池分配器)MyPoolAllocator<char> pool_alloc;// 3. 打开文件(资源)FILE* fp = std::fopen("test.txt", "w");if (!fp) return 1;// 4. 用该构造函数创建 shared_ptr:管理 FILE* + 自定义删除器 + 内存池分配器std::shared_ptr<FILE> file_ptr(fp, file_deleter, pool_alloc);// 使用资源(无需手动释放文件或控制块)std::fputs("hello, shared_ptr", file_ptr.get());return 0;
}
// 输出(程序结束时,shared_ptr 析构触发释放):
// 文件已关闭
4.复制构造与移动构造
template <class _Ty2>
shared_ptr(const shared_ptr<_Ty2>& _Right, element_type* _Px) noexcept {// construct shared_ptr object that aliases _Rightthis->_Alias_construct_from(_Right, _Px);
}template <class _Ty2>
shared_ptr(shared_ptr<_Ty2>&& _Right, element_type* _Px) noexcept {// move construct shared_ptr object that aliases _Rightthis->_Alias_move_construct_from(_STD move(_Right), _Px);
}shared_ptr(const shared_ptr& _Other) noexcept { // construct shared_ptr object that owns same resource as _Otherthis->_Copy_construct_from(_Other);
}template <class _Ty2, enable_if_t<_SP_pointer_compatible<_Ty2, _Ty>::value, int> = 0>
shared_ptr(const shared_ptr<_Ty2>& _Other) noexcept {// construct shared_ptr object that owns same resource as _Otherthis->_Copy_construct_from(_Other);
}shared_ptr(shared_ptr&& _Right) noexcept { // construct shared_ptr object that takes resource from _Rightthis->_Move_construct_from(_STD move(_Right));
}template <class _Ty2, enable_if_t<_SP_pointer_compatible<_Ty2, _Ty>::value, int> = 0>
shared_ptr(shared_ptr<_Ty2>&& _Right) noexcept { // construct shared_ptr object that takes resource from _Rightthis->_Move_construct_from(_STD move(_Right));
}template <class _Ty2, enable_if_t<_SP_pointer_compatible<_Ty2, _Ty>::value, int> = 0>
explicit shared_ptr(const weak_ptr<_Ty2>& _Other) { // construct shared_ptr object that owns resource *_Otherif (!this->_Construct_from_weak(_Other)) {_Throw_bad_weak_ptr();}
}
- 复制构造(如
shared_ptr(const shared_ptr& _Other)):调用_Copy_construct_from(_Other),本质是对_Other的控制块_Rep的引用计数执行原子递增(确保线程安全),共享同一控制块。 - 移动构造(如
shared_ptr(shared_ptr&& _Right)):调用_Move_construct_from(_STD move(_Right)),不修改引用计数,仅转移_Ptr和_Rep的所有权(_Right会被置空)。 - 其它构造函数都类似,就不一一介绍了。
5.从weak_ptr构造
构造函数explicit shared_ptr(const weak_ptr<_Ty2>& _Other)通过_Construct_from_weak(_Other)实现:
- 检查
weak_ptr指向的控制块是否有效(对象未被释放)。 - 若有效,递增控制块的引用计数(从
weak_count转为use_count),否则抛出bad_weak_ptr异常。
6.从 std::unique_ptr<T> 转换而来
template <class _Ux, class _Dx,enable_if_t<conjunction_v<_SP_pointer_compatible<_Ux, _Ty>,is_convertible<typename unique_ptr<_Ux, _Dx>::pointer, element_type*>>,int> = 0>shared_ptr& operator=(unique_ptr<_Ux, _Dx>&& _Right) { // move from unique_ptrshared_ptr(_STD move(_Right)).swap(*this);return *this;}
- 当
unique_ptr被移动到shared_ptr时(shared_ptr构造函数接收unique_ptr&&),unique_ptr会释放所有权,shared_ptr会为该对象创建新的控制块,并复用unique_ptr的删除器。
3.8.3.析构函数与引用计数递减
析构函数~shared_ptr() noexcept调用this->_Decref(),这是释放资源的核心逻辑:
_Decref对控制块的use_count执行原子递减。- 若递减后
use_count为 0:调用控制块中的删除器释放管理对象(_Ptr指向的资源)。 - 若此时控制块的
weak_count也为 0:释放控制块自身内存(避免控制块泄漏)。
3.8.4.reset操作:所有权转移
reset方法通过 “创建新shared_ptr并交换” 实现(如shared_ptr(_Px).swap(*this)):
- 新
shared_ptr关联新资源(或空),旧shared_ptr(*this)的控制块在交换后被析构,触发_Decref(递减旧资源的引用计数)。 - 最终
*this关联新资源,完成所有权转移。
3.8.5.控制块的具体类型
代码中出现的_Ref_count<_Ux>、_Ref_count_resource等是控制块的具体实现(均继承自_Ref_count_base):
_Ref_count<_Ux>:用于默认删除器(delete或delete[]),存储use_count、weak_count和对象指针。_Ref_count_resource<_UxptrOrNullptr, _Dx>:用于自定义删除器,额外存储删除器_Dx,确保资源释放时调用自定义逻辑。
| 控制块类 | 对象存储方式 | 分配器支持 | 自定义删除器支持 | 核心场景 |
|---|---|---|---|---|
_Ref_count | 外部指针 | 否 | 否 | 无删除器、默认分配器 |
_Ref_count_resource | 外部指针 | 否 | 是 | 有删除器、默认分配器 |
_Ref_count_resource_alloc | 外部指针 | 是 | 是 | 有删除器、自定义分配器 |
_Ref_count_obj2 | 嵌入控制块 | 否 | 否 | 无删除器、make_shared |
_Ref_count_obj_alloc3 | 嵌入控制块 | 是 | 否 | 无删除器、allocate_shared |
3.9.std::make_shared实现分析
_NODISCARD_SMART_PTR_ALLOC shared_ptr<_Ty> make_shared(_Types&&... _Args) { // make a shared_ptr to non-array objectconst auto _Rx = new _Ref_count_obj2<_Ty>(_STD forward<_Types>(_Args)...);shared_ptr<_Ty> _Ret;_Ret._Set_ptr_rep_and_enable_shared(_STD addressof(_Rx->_Storage._Value), _Rx);return _Ret;
}
通过 _Ref_count_obj2 控制块实现 “控制块 + 对象一体化内存分配”,仅需 1 次内存分配(而非普通构造的 2 次),高效创建 shared_ptr,同时自动绑定对象与控制块,支持 enable_shared_from_this。
make_shared 的核心优势(对比普通 shared_ptr 构造)
普通 shared_ptr 构造(如 shared_ptr<_Ty>(new _Ty(...)))需要 2 次内存分配:
new _Ty(...):分配_Ty对象内存。- 内部
new _Ref_count<_Ty>(...):分配控制块内存。
而 make_shared 仅需 1 次内存分配(new _Ref_count_obj2<_Ty>),带来两个关键优势:
- 性能更优:减少 1 次内存分配 / 释放的系统调用开销,降低内存碎片风险。
- 缓存 locality 更好:对象与控制块存储在连续内存中,CPU 缓存命中率更高,访问速度更快。
make_shared 的实现核心是 “_Ref_count_obj2 控制块 + 一体化内存分配 + 完美转发”:
- 1 次内存分配替代 2 次,提升性能、减少碎片。
- 原地构造对象 + 完美转发,避免冗余拷贝。
- 自动绑定控制块与对象,支持
enable_shared_from_this,兼顾易用性与安全性。
这也是 make_shared 被推荐作为 shared_ptr 首选创建方式的根本原因 —— 高效且简洁。
3.10.std::enable_shared_from_this实现分析
_EXPORT_STD template <class _Ty>
class enable_shared_from_this { // provide member functions that create shared_ptr to this
public:using _Esft_type = enable_shared_from_this;_NODISCARD shared_ptr<_Ty> shared_from_this() {return shared_ptr<_Ty>(_Wptr);}_NODISCARD shared_ptr<const _Ty> shared_from_this() const {return shared_ptr<const _Ty>(_Wptr);}_NODISCARD weak_ptr<_Ty> weak_from_this() noexcept {return _Wptr;}_NODISCARD weak_ptr<const _Ty> weak_from_this() const noexcept {return _Wptr;}protected:constexpr enable_shared_from_this() noexcept : _Wptr() {}enable_shared_from_this(const enable_shared_from_this&) noexcept : _Wptr() {// construct (must value-initialize _Wptr)}enable_shared_from_this& operator=(const enable_shared_from_this&) noexcept { // assign (must not change _Wptr)return *this;}~enable_shared_from_this() = default;private:template <class _Yty>friend class shared_ptr;mutable weak_ptr<_Ty> _Wptr;
};
std::enable_shared_from_this<_Ty> 是一个模板基类,其核心作用是让派生类能够安全地返回指向自身的 shared_ptr,避免直接使用 this 指针构造 shared_ptr 导致的重复释放问题。其源码实现围绕 “用 weak_ptr 跟踪管理当前对象的 shared_ptr 控制块” 展开。
3.10.1.核心定位与设计目标
当类 T 继承 enable_shared_from_this<T> 后,T 的对象可以通过 shared_from_this() 方法返回一个与管理该对象的 shared_ptr 共享引用计数的 shared_ptr,从而:
- 避免直接用
this构造shared_ptr导致的 “多控制块重复释放” 问题(见上一节分析)。 - 支持在类内部安全地将自身作为
shared_ptr传递(如回调函数、异步操作等场景)。
3.10.2.关键成员与友元关系
1.私有成员:_Wptr(核心跟踪器)
mutable weak_ptr<_Ty> _Wptr; // 弱引用,跟踪管理当前对象的 shared_ptr 控制块
mutable修饰:允许在const成员函数中修改(如const版本的shared_from_this())。- 作用:
_Wptr会被关联到管理当前对象的shared_ptr控制块,通过它可以安全生成指向自身的shared_ptr(不创建新控制块)。
2.友元类:shared_ptr
template <class _Yty>
friend class shared_ptr;
- 允许
shared_ptr访问_Wptr,在shared_ptr管理_Ty对象时,自动初始化_Wptr使其绑定到正确的控制块(这是核心机制的关键)。
3.10.3.核心机制:_Wptr 如何关联到控制块?
enable_shared_from_this 的关键是 _Wptr 必须被正确关联到管理当前对象的 shared_ptr 控制块,这一过程由 shared_ptr 的构造函数自动完成(因 shared_ptr 是友元)。
以 make_shared<T>() 为例,流程如下:
1.make_shared<T>() 创建 T 对象(继承自 enable_shared_from_this<T>),并生成控制块 _Ref_count_obj2<T>。
template <class _Yty, class = void>
struct _Can_enable_shared : false_type {}; // detect unambiguous and accessible inheritance from enable_shared_from_thistemplate <class _Yty>
struct _Can_enable_shared<_Yty, void_t<typename _Yty::_Esft_type>>: is_convertible<remove_cv_t<_Yty>*, typename _Yty::_Esft_type*>::type {// is_convertible is necessary to verify unambiguous inheritance
};
2.shared_ptr<T> 的构造函数检测到 T 继承自 enable_shared_from_this<T>,于是执行类似以下的逻辑:
template <class _Ux>void _Set_ptr_rep_and_enable_shared(_Ux* const _Px, _Ref_count_base* const _Rx) noexcept { // take ownership of _Pxthis->_Ptr = _Px;this->_Rep = _Rx;if constexpr (conjunction_v<negation<is_array<_Ty>>, negation<is_volatile<_Ux>>, _Can_enable_shared<_Ux>>) {if (_Px && _Px->_Wptr.expired()) {_Px->_Wptr = shared_ptr<remove_cv_t<_Ux>>(*this, const_cast<remove_cv_t<_Ux>*>(_Px));}}}
3.此时,_Wptr 已绑定到管理 T 对象的控制块,后续调用 shared_from_this() 时,就能从 _Wptr 生成共享计数的 shared_ptr。
3.10.4.使用限制与注意事项
1.必须先被 shared_ptr 管理
调用 shared_from_this() 前,对象必须已被 shared_ptr 持有(如通过 make_shared 或 shared_ptr 构造)。否则 _Wptr 为空,调用会抛出 bad_weak_ptr 异常。
T obj; // 错误:obj 未被 shared_ptr 管理
auto sp = obj.shared_from_this(); // 抛出 bad_weak_ptr
2.禁止在构造函数中调用
构造函数执行时,shared_ptr 尚未完成对对象的管理(_Wptr 未绑定控制块),此时调用 shared_from_this() 会导致 _Wptr 为空,触发异常。
class T : public enable_shared_from_this<T> {
public:T() {auto sp = shared_from_this(); // 错误:构造中 _Wptr 未初始化}
};
3.拷贝对象需重新被 shared_ptr 管理
拷贝构造出的新对象的 _Wptr 为空,需重新通过 shared_ptr 管理后才能调用 shared_from_this()。
4.shared_ptr的线程安全特性理解
1.引用计数的操作是线程安全的
shared_ptr内部维护着一个引用计数(用于跟踪当前有多少个shared_ptr实例指向同一个对象)。C++ 标准规定,对引用计数的修改(如复制shared_ptr时的计数递增,或销毁shared_ptr时的计数递减)是原子操作,因此多个线程同时对不同的shared_ptr实例进行复制、赋值或销毁(这些操作会修改共享的引用计数)时,不会导致竞态条件,是线程安全的。
2.对shared_ptr实例本身的修改不是线程安全的
如果多个线程同时操作同一个shared_ptr实例(例如,对同一个shared_ptr变量进行赋值、重置等修改操作),则这些操作不是线程安全的。因为这类操作不仅会修改引用计数,还会修改shared_ptr内部指向对象的指针,而标准未保证这些操作的原子性,可能导致竞态条件。
例如:
std::shared_ptr<int> ptr = std::make_shared<int>(42);// 线程1
ptr = std::make_shared<int>(100); // 修改ptr本身// 线程2
ptr = std::make_shared<int>(200); // 同时修改同一个ptr
这种情况下,线程 1 和线程 2 同时修改同一个shared_ptr实例,可能导致未定义行为,需要通过互斥锁等同步机制保证安全。
3.指向的对象本身的线程安全与shared_ptr无关
shared_ptr仅管理对象的生命周期(通过引用计数),不保证对其指向的对象的访问是线程安全的。如果多个线程通过shared_ptr访问或修改所指向的对象,仍需通过互斥锁等机制确保对象操作的线程安全。
总结:
shared_ptr的引用计数操作是线程安全的(原子操作)。- 对同一个
shared_ptr实例的修改操作(如赋值、重置)不是线程安全的,需外部同步。 - 对
shared_ptr指向的对象的访问,线程安全需用户自行保证。
