当前位置: 首页 > news >正文

C++中的智能指针std::shared_ptr是线程安全的吗?以及它的详细实现原理

目录

1.std::shared_ptr的使用

1.1.基础用法:创建与初始化

1.2.共享所有权:复制与赋值

1.3.访问与操作管理的资源

1.4.释放资源:reset() 与析构

1.5.自定义删除器:管理非内存资源

1.6.与 weak_ptr 配合:解决循环引用

1.7.利用std::enable_shared_from_this返回自身

2.std::weak_ptr的使用

2.1.核心特性与定位

2.2.基本用法:创建与转换

2.3.典型应用场景

2.4.注意事项

3.std::shared_ptr的具体实现原理

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.std::shared_ptr实现分析

3.8.1.继承关系与核心成员

3.8.2.构造函数:控制块的创建与初始化

3.8.3.析构函数与引用计数递减

3.8.4.reset操作:所有权转移

3.8.5.控制块的具体类型

3.9.std::make_shared实现分析

3.10.std::enable_shared_from_this实现分析

3.10.1.核心定位与设计目标

3.10.2.关键成员与友元关系

3.10.3.核心机制:_Wptr 如何关联到控制块?

3.10.4.使用限制与注意事项

4.shared_ptr的线程安全特性理解


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 成功)。
  • 实现逻辑:
    1. 原子读取当前 _Uses 值(_Volatile_uses 确保无编译优化)。
    2. 循环用 CAS 操作(_InterlockedCompareExchange)尝试递增 _Uses,避免多线程竞态。
    3. 若 _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()
    1. _MT_DECR(_Uses) 后 _Uses=0,调用 _Destroy() 释放被观察者对象。
    2. 调用 _Decwref()_Weaks 递减为 1(观察者的 weak_ptr 仍持有)。
    3. 此时 _Weaks 不为 0,控制块不释放(仍支撑观察者检测状态)。

5.观察者释放:控制块最终释放

当观察者也被释放时:

observer.reset();  // 观察者的 weak_ptr 析构,触发 _Decwref()
  • 底层:weak_ptr 析构时调用 _Decwref(),被观察者的控制块 _Weaks 递减为 0。
  • 此时 _Uses 已为 0,_Decwref() 调用 _Delete_this(),释放控制块本身(无内存泄漏)。

3.2.3.核心优势:_Ref_count_base 如何解决观察者模式的痛点

  1. 不延长被观察者生命周期:观察者持有 weak_ptr 仅操作 _Weaks 计数,不影响 _Uses,被观察者的生命周期由业务逻辑的 shared_ptr 决定(_Uses 为 0 即释放)。
  2. 避免野指针lock() 依赖 _Incref_nz() 检测 _Uses,确保仅在被观察者存活时才访问,杜绝野指针访问。
  3. 线程安全_Uses 和 _Weaks 均为原子类型,增减操作通过 _MT_INCR/_MT_DECR(原子函数)实现,支持多线程下的注册、通知、释放。
  4. 无内存泄漏:控制块仅在 _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 等通用逻辑)。
  • 核心职责
    1. 存储自定义删除器(_Dx)、资源(_Resource)和自定义分配器(_Alloc)。
    2. 通过删除器释放资源,通过分配器释放控制块自身内存(替代默认 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);           // 用分配器释放控制块内存
}
  1. 获取分配器:从内层 _Compressed_pair 中取出 _Myalty 分配器实例 _Al
  2. 显式析构:调用 this->~_Ref_count_resource_alloc(),销毁控制块的成员(删除器、分配器、资源指针等);
  3. 释放内存:通过 _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.核心定位与适用场景

  • 父类组合
    1. _Ref_count_base:继承原子引用计数、_Decref/_Decwref 等通用逻辑。
    2. _Ebco_base<_Rebind_alloc_t<_Alloc, _Ty>>_Ebco_base 是 “空基类优化(EBO)” 基类,用于存储重绑定后的分配器,避免空分配器占用额外内存。
  • 核心特征
    1. 对象嵌入控制块:_Ty 类型对象直接存储在控制块内部,不单独分配内存。
    2. 分配器管理内存:控制块(含嵌入对象)的内存由用户指定的自定义分配器分配 / 释放。
  • 适用场景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)...);
}
  • 初始化父类:
    1. _Ebco_base<_Rebound>(_Al_arg):将自定义分配器 _Al_arg 存储到基类(EBO 优化,无额外内存开销)。
    2. _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);  // 用分配器释放控制块内存
}
  • 核心差异:控制块内存由自定义分配器分配,因此释放时需遵循 “分配 - 释放匹配” 原则:
    1. 重绑定分配器:将原始分配器 _Alloc 适配为能释放 _Ref_count_obj_alloc3 类型的分配器 _Al
    2. 显式析构:调用控制块的析构函数,销毁内部成员(如分配器、_Storage)。
    3. 释放内存:通过 _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
  • 底层动作:
    1. 分配器 alloc 被重绑定为 _Rebound(适配 MyClass 构造)和 _Rebind_alloc_t<_Alloc, _Ref_count_obj_alloc3>(适配控制块分配)。
    2. 用重绑定后的分配器分配一块连续内存,大小 = 控制块大小 + MyClass 对象大小。
    3. 在该内存中构造 _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>:用于默认删除器(deletedelete[]),存储use_countweak_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 次内存分配:

  1. new _Ty(...):分配 _Ty 对象内存。
  2. 内部 new _Ref_count<_Ty>(...):分配控制块内存。

而 make_shared 仅需 1 次内存分配(new _Ref_count_obj2<_Ty>),带来两个关键优势:

  1. 性能更优:减少 1 次内存分配 / 释放的系统调用开销,降低内存碎片风险。
  2. 缓存 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,从而:

  1. 避免直接用 this 构造 shared_ptr 导致的 “多控制块重复释放” 问题(见上一节分析)。
  2. 支持在类内部安全地将自身作为 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指向的对象的访问,线程安全需用户自行保证。
http://www.dtcms.com/a/581785.html

相关文章:

  • 网站服务器安装教程视频教程电子商务网站规划
  • 【vsftpd报错】227 Entering Passive Mode,553 Could not create file.
  • 有多少网站可以推广业务那个公司做app
  • 正规的大连网站建设a963中华室内设计官网
  • 中承信安信创软件检测:CMA资质+国家标准双重保障的测试报告
  • #智能CI/CD流水线与AIOps 论坛@AiDD深圳站
  • 医疗AI模型与控制器自动化CI/CD流水线
  • NumPy -数组运算与操作
  • 中美最近军事新闻邯郸网站优化公司
  • windows本机vscode通过ssh免密登录远程linux服务器 git push/pull 免密
  • go语言网站开发教程门户网站是如何做引流的
  • SG-ECAT_S-TCP(EtherCAT 转 ModbusTCP 网关)
  • 分享一些在C++中使用异常处理的最佳实践
  • 物流网站怎么开网络最好的运营商
  • 学习随笔-async和await
  • 祁阳做网站河南工程建设验收公示网
  • PCIe协议分析仪-VIAVI设置抓取ASPM协商过程
  • ThreadLocal 相关知识点
  • OSG新版GLSL语法全解析
  • 智守边界:入侵报警系统的主动防御时代
  • 为什么网站建设起来搜素不到电子商务网站建设考题
  • 济南网站建设是什么合肥seo网络营销推广
  • 【Hot100|4-LeetCode 283. 移动零 】
  • 操作系统拿着文件名查找磁盘文件的全过程
  • 【Hot100 | 6 LeetCode 15. 三数之和】
  • 哪些网站用wordpress建设银行网站总是崩溃
  • c#实现redis的调用与基础类
  • 【深度学习新浪潮】什么是投机解码?大模型推理优化的核心技术解析(含代码实操)
  • Verilog函数function
  • 做电商宠物带哪个网站最好网络营销方法的选择