我们之前说过weak_ptr可以用来解决share_ptr循环引用的问题,先来看看循环引用问题
struct Node {
std::shared_ptr<Node> next;
};
auto node1 = std::make_shared<Node>();
auto node2 = std::make_shared<Node>();
node1->next = node2;
node2->next = node1; // 循环引用
两个share指针指向的资源中的share指针指向对方,这样析构的时候,两个指针的强引用计数都从2减到1,不为0,资源没有释放
改为weak指针,只引用不计数,不增加share指针中的强引用计数,可以正常释放资源
struct Node {
std::weak_ptr<Node> next;
};
auto node1 = std::make_shared<Node>();
auto node2 = std::make_shared<Node>();
node1->next = node2;
node2->next = node1;
下面看用普通指针能否解决
struct Node {
std::shared_ptr<Node>*next;
};
auto node1 = std::make_shared<Node>();
auto node2 = std::make_shared<Node>();
node1->next = &node2;
node2->next = &node1;
用普通指针确实没有增加share指针的强引用计数,表面上看似解决了循环引用问题,实际上带来了普通指针的内存安全问题
此外weak_ptr可以通过lock()获取一个share_ptr,通过expired()获知资源是否还在,另外weak指针会进行弱引用计数
每个资源会对应一个控制块,在make_share指针的时候会给资源分配一个控制块,即所有指向同一对象的shared_ptr
和weak_ptr
共享同一个控制块
+----------------------+
| 控制块结构 |
|----------------------|
| 强引用计数 (shared) | ← 记录有多少个shared_ptr指向该对象
| 弱引用计数 (weak) | ← 记录有多少个weak_ptr依赖此控制块
| 原始指针 | ← 实际管理的对象指针
| 删除器 (deleter) | ← 自定义删除函数(如果有)
| 分配器 (allocator) | ← 自定义分配器(如果有)
+----------------------+
强引用计数用于管理资源,计数为0时释放资源
弱引用计数用于管理控制块,当强引用计数和弱引用计数均为0时释放控制块
控制块在资源释放时仍然存在是为了方便weak_ptr查询资源状态