关键词控制块、引用计数、weak_ptr、原子操作、make_shared、循环引用适合人群已理解所有权模型与移动语义想深入理解 shared_ptr 内部实现的开发者一、为什么要单独研究 shared_ptr在前两篇我们讲清unique_ptr 独占所有权移动语义 所有权转移机制但 shared_ptr 不一样。它允许auto p1 std::make_sharedStudent(18); auto p2 p1;多个对象共同持有同一资源。问题来了shared_ptr 如何保证最后一个释放时才 delete如何避免 double free为什么会有循环引用问题这一篇我们从实现角度拆解。二、shared_ptr 内部结构控制块Control Blockshared_ptr 并不只是一个“指针”。它内部包含两部分shared_ptr ↓ 指向对象的指针 控制块Control Block控制块通常包含- 强引用计数strong count - 弱引用计数weak count - 删除器deleter - 可能还有分配器信息可以简单理解为对象本体 控制块管理对象生命周期关键点shared_ptr 之间共享的不是对象本体而是共享“控制块”。三、引用计数是如何工作的当你写auto p1 std::make_sharedStudent(18); auto p2 p1;发生了什么1️⃣ 创建对象2️⃣ 创建控制块3️⃣ strong_count 14️⃣ p2 拷贝后 → strong_count 2当 p2 析构strong_count 1当 p1 析构strong_count 0 → 删除对象注意对象删除发生在 strong_count 0 时。四、弱引用计数是干什么的weak_ptr 的存在让控制块多了一层复杂性。控制块有两个计数类型含义strong_count拥有对象的数量weak_count观察对象的数量当 strong_count 0对象本体被 delete但控制块不会立即释放只有当strong_count 0 且 weak_count 0控制块才被销毁。这保证weak_ptr 仍然可以安全检测对象是否存在。五、为什么引用计数必须是原子操作shared_ptr 是线程安全的计数层面。多个线程可能同时拷贝 shared_ptr销毁 shared_ptr如果 strong_count 不是原子操作两个线程同时减 1可能只减一次或者减成负数或者提前 delete因此标准库实现中引用计数通常使用原子变量。这就是 shared_ptr 的额外成本。六、为什么 make_shared 更高效对比两种写法std::shared_ptrStudent p(new Student(18));和auto p std::make_sharedStudent(18);区别第一种分配一次对象内存再分配一次控制块内存两次堆分配第二种对象和控制块一起分配一次堆分配内存更紧凑性能更好工程最佳实践优先使用 make_shared。七、循环引用shared_ptr 最大的坑看一个经典例子class B; class A { public: std::shared_ptrB b; }; class B { public: std::shared_ptrA a; };当你创建auto a std::make_sharedA(); auto b std::make_sharedB(); a-b b; b-a a;发生了什么A 持有 Bstrong_count 1B 持有 Astrong_count 1当外部 shared_ptr 释放后A strong_count 1B strong_count 1永远不为 0。对象不会释放。这就是循环引用导致内存泄漏。八、weak_ptr 的真正价值解决方法class B { public: std::weak_ptrA a; };weak_ptr不增加 strong_count不参与对象生命周期只是观察者当需要访问对象时if (auto sp a.lock()) { // 安全访问 }这才是weak_ptr 存在的真正原因。九、shared_ptr 的工程代价shared_ptr 很强大但不是免费的。代价包括1️⃣ 原子引用计数开销2️⃣ 控制块额外内存3️⃣ 循环引用风险4️⃣ 复杂性提升因此工程实践中优先使用 unique_ptr只有在确实需要共享时才用 shared_ptr十、shared_ptr 的核心设计哲学shared_ptr 本质是“生命周期延迟释放机制”它解决的问题是多个模块共同拥有资源时如何安全释放它不适用于简单局部对象单一所有者场景性能极致场景十一、机制 实现串联总结到这里我们已经建立了三层理解第一篇认知层→ 所有权模型第二篇机制层→ 移动语义与所有权转移第三篇实现层→ 控制块 引用计数 weak_ptr现在你已经可以回答shared_ptr 如何避免 double free为什么有 strong / weak 两种计数为什么 make_shared 更优为什么循环引用会发生这已经进入STL 设计理解层。下一篇预告在第四篇我们会做体系整合现代 C 内存管理全景图内容包括栈 vs 堆RAIIRule of Fiveunique / shared / weak 使用准则工程最佳实践清单所有权模型完整结构图三篇铺垫之后进入体系。智能指针四体系篇 —— 现代 C 内存管理全景图