weak_ptr是否有计数方式,在哪分配的空间。
目录
前言
1. weak_ptr 的引用计数问题
2. 控制块和内存分配
前言
weak_ptr
不增加对象的强引用计数,但会增加控制块的弱引用计数。- 如果使用
make_shared
,控制块和对象在同一块内存中;使用new
时,控制块和对象分配在不同的内存区域。weak_ptr
通过shared_ptr
的控制块管理弱引用计数,避免了循环引用的内存泄漏问题。
1. weak_ptr
的引用计数问题
“weak_ptr 真的不计数吗?”
严格来说,weak_ptr
确实不会增加对象的强引用计数。然而,这并不意味着它完全不涉及计数。事实上,weak_ptr
会参与弱引用计数的管理。
引用计数的细节
-
强引用计数(use count):用于跟踪当前有多少个
shared_ptr
指向同一个对象。每创建一个新的shared_ptr
,强引用计数就增加;每销毁一个shared_ptr
,强引用计数就减少。当强引用计数为零时,管理的对象会被销毁。 -
弱引用计数(weak count):用于跟踪当前有多少个
weak_ptr
指向同一个对象的控制块。弱引用计数不影响对象的生命周期,只是管理控制块的存在。当强引用计数和弱引用计数都为零时,控制块本身才会被销毁。
#include <iostream>
#include <memory>
int main() {
std::shared_ptr<int> sp = std::make_shared<int>(10);
std::weak_ptr<int> wp = sp;
std::cout << "use_count: " << sp.use_count() << std::endl; // 输出 1
std::cout << "weak_ptr use_count: " << wp.use_count() << std::endl; // 输出 1
if (auto spt = wp.lock()) { // 转换为 shared_ptr,增加强引用计数
std::cout << "use_count after lock: " << sp.use_count() << std::endl; // 输出 2
}
std::cout << "use_count after lock is released: " << sp.use_count() << std::endl; // 输出 1
}
在这个示例中:
sp.use_count()
返回的是强引用计数(此处为1)。wp.use_count()
返回的也是强引用计数(此处为1),但wp
本身不增加这个计数。wp.lock()
返回一个新的shared_ptr
,临时增加强引用计数,当这个临时shared_ptr
释放时,强引用计数恢复。
2. 控制块和内存分配
“计数和控制块在哪里分配的空间?”
控制块(control block)是一个独立的结构体,通常包含以下内容:
- 强引用计数(use count)
- 弱引用计数(weak count)
- 被管理的对象的指针
- 自定义的析构函数等
内存分配的方式有两种:
1.使用 make_shared
初始化:
当你使用 make_shared
时,控制块和实际对象通常分配在同一块内存中。这种方式优化了内存分配,减少了分配的次数,提高了性能。
auto sp = std::make_shared<int>(10);
这里,sp
的控制块和它管理的 int
对象是分配在一起的。
2.使用 new
和 shared_ptr
构造函数初始化:
如果你使用 new
创建对象,然后用 shared_ptr
构造函数管理这个对象,控制块和对象的内存是分开分配的。控制块由 shared_ptr
管理,但它与对象不在同一块内存中。
auto sp = std::shared_ptr<int>(new int(10));
-
这里,
sp
的控制块与int
对象位于不同的内存区域。
3. weak_ptr
与 shared_ptr
的控制块关系
weak_ptr
共享 shared_ptr
的控制块,它不持有资源,但持有对控制块的弱引用。控制块中的弱引用计数用于确定何时可以安全地销毁控制块,而不是销毁对象本身。