C++智能指针控制块的基本概念
C++标准库中的shared_ptr和weak_ptr都依赖控制块实现内存管理功能,控制块是一块独立分配的内存区域,用于存储管理动态对象所需的全部元数据。当使用shared_ptr接管一个动态对象时,会同时创建对应的控制块,后续所有指向同一对象的shared_ptr和weak_ptr都会共享这个控制块。

控制块的核心内部结构
1. 强引用计数
强引用计数用于记录当前有多少个shared_ptr指向被管理的对象。当强引用计数降为0时,会自动释放被管理的动态对象内存。强引用计数的初始值通常为1,每次拷贝shared_ptr时会加1,每次shared_ptr销毁时会减1。
2. 弱引用计数
弱引用计数用于记录当前有多少个weak_ptr指向该控制块。弱引用计数不影响被管理对象的生命周期,只有当强引用计数和弱引用计数都降为0时,控制块本身的内存才会被释放。这个设计解决了weak_ptr需要访问控制块但不需要延长对象生命周期的需求。
3. 删除器
删除器是一个可调用对象,用于控制被管理对象的释放方式。默认情况下删除器会调用delete释放对象,但用户也可以自定义删除器,比如用于释放数组、文件句柄等特殊资源。删除器会存储在控制块中,当强引用计数降为0时会被调用。
4. 分配器
分配器用于控制控制块本身以及被管理对象的内存分配和释放方式,默认使用std::allocator,用户也可以自定义分配器来满足特殊的内存管理需求。分配器会和删除器一起存储在控制块中。
5. 被管理对象存储(可选)
如果创建shared_ptr时使用make_shared,被管理的对象会和控制块分配在同一块连续内存中,这种情况下控制块内部会包含对象的存储区域,能够减少一次内存分配操作,提升性能。如果是直接用shared_ptr接管已有的原始指针,对象和控制块是分开分配的。
控制块的工作流程示例
下面通过代码示例展示控制块相关计数的变化过程:
#include <iostream>
#include <memory>
int main() {
// 创建shared_ptr,同时创建控制块,强引用计数初始为1,弱引用计数为0
std::shared_ptr<int> ptr1 = std::make_shared<int>(10);
std::cout << "初始强引用计数: " << ptr1.use_count() << std::endl; // 输出1
// 拷贝shared_ptr,强引用计数加1
std::shared_ptr<int> ptr2 = ptr1;
std::cout << "拷贝后强引用计数: " << ptr1.use_count() << std::endl; // 输出2
// 创建weak_ptr,弱引用计数加1,强引用计数不变
std::weak_ptr<int> weak_ptr = ptr1;
std::cout << "创建weak_ptr后强引用计数: " << ptr1.use_count() << std::endl; // 输出2
// 销毁ptr2,强引用计数减1
ptr2.reset();
std::cout << "销毁ptr2后强引用计数: " << ptr1.use_count() << std::endl; // 输出1
// 销毁ptr1,强引用计数降为0,释放被管理的int对象
ptr1.reset();
// 此时弱引用计数仍为1,控制块不会被释放
std::cout << "ptr1销毁后weak_ptr是否过期: " << weak_ptr.expired() << std::endl; // 输出true,因为对象已被释放
return 0;
}
控制块的设计意义
控制块将引用计数、删除器、分配器等元数据集中存储,让多个智能指针可以高效共享管理信息,避免了每个智能指针单独存储管理数据的冗余。同时分离对象生命周期和控制块生命周期的设计,让weak_ptr可以在不延长对象生命周期的前提下观察对象状态,有效解决了shared_ptr的循环引用问题。理解控制块的内部结构,能够帮助开发者更合理地使用智能指针,写出更健壮的内存管理代码。
shared_ptrweak_ptr控制块引用计数修改时间:2026-06-23 03:33:31