weak_ptr
前提
好的你现在已经知道了unique_ptr和shared_ptr两种指针,使用确保指针的独享,shared_ptr确保指针的共享,那么weak_ptr是干嘛的?
作用
既然shared_ptr是内部包含引用计数的强引用,那么有这样需求:
- 不对资源进行 管理,但是可以使用这个堆对象机能
- 可以知道这个堆对象是否被析构
正确的使用方式
其实可以把weak_ptr看作shared_ptr的扩充,weak_ptr无法进行解引用
-
weak_ptr是从shared_ptr创建而来,但是不改变shared_ptr内部的引用计数
-
怎么使用weak_ptr呢?weak_ptr自带一个expired()判断引用计数是否为0
shared_ptr<int> test(make_shared<int>()); weak_ptr<int> weak(test); test.reset(); std::cout << weak.expired() << std::endl;
那么你也可能会想使用解引用的方式得到内部指针,首先weak_ptr不允许这种行为。就算允许容易引起条件竞争:
在 weak.expired() 和 解引用 中间,另一个线程将这片内存释放,这样就出现未定义行为了
-
你需要的是 原子性判断堆对象是否存在,存在则返回shared_ptr,不存在返回空shared_ptr
lock出现了,内部并不是很复杂的操作
shared_ptr<_Tp> lock() const noexcept { return this->expired() ? shared_ptr<_Tp>() : shared_ptr<_Tp>(*this); }
-
正确的使用方式
shared_ptr<int> test(make_shared<int>()); weak_ptr<int> weak(test); shared_ptr<int> test_weak = weak.lock(); if (test_weak) { std::cout << "use lock get shared" << std::endl; }
使用场景
-
对象池
保存已有对象的资源,当下一次相同的需求到来,直接返回对象池的内容
方案是这样,使用ID值标识每个资源,当ID不存在则创建资源,当ID存在直接返回资源。当没人指向这个资源将ID销毁
#include <iostream> // std::streambuf, std::cout #include <memory> // std::ofstream #include <functional> // std::ofstream #include <unordered_map> // std::ofstream using std::shared_ptr; using std::weak_ptr; using std::unordered_map; using std::unique_ptr; template <typename T, typename ...Args> std::unique_ptr<T> make_unique(Args&& ...args) { return unique_ptr<T>(new T(std::forward<Args>(args)...)); } shared_ptr<const int> LoadFastFactory(int id) { static unordered_map<int, weak_ptr<const int>> cache; auto cache_iter = cache.find(id); shared_ptr<const int> cache_ptr; if (cache_iter != cache.end()) { cache_ptr = (cache_iter->second).lock(); } if (!cache_ptr) { cache_ptr = make_unique<const int>(id); cache[id] = cache_ptr; } return cache_ptr; } int main () { shared_ptr<const int> test_1 = LoadFastFactory(1); shared_ptr<const int> test_2 = LoadFastFactory(1); std::cout << (test_1 == test_2) << std::endl; return 0; }
使用 make_unique进行资源的创建
cache进行资源的快速缓存获取
shared_ptr用于在没人使用资源的时候进行资源的自动析构,上述代码有问题,在于即使资源被删除,ID仍旧存在
- Observer design pattern
观察者模式的核心就是 observable将需要通知的事情通知每个Observer,在这期间还需要确保Observer不是destory的
实现起来就是,observable用weak_ptr存放Observer,这样也带来限制 Observer也是用shared_ptr管理的
-
循环引用
A内部持有shared_ptr的B
C内部持有shared_ptr的B
B需要使用A的功能,那么请问A应该是什么样的形式存在呢?
- 原始指针?,A可能存在销毁的可能性,带来悬挂引用的危险
- shared_ptr ? A 持有 B,B 持有 A,导致互相的shared_ptr计数都为1,无法释放堆内存
- weak_ptr ?可行,因为waek_ptr不参与共享指针的引用计数
实际上,在工程中对于这种例子,本质上是类之间结构的问题
首先需要明确一点:使用shared_ptr就是在管理堆对象的生命周期,使用weak_ptr不负责管理堆对象的使用周期
工程上一般对这种方式处理循环引用是很少的,为啥呢,因为所有出现循环引用的问题都是因为类设计问题,也就是说保证 类之间的耦合关系,就像一个Tree结构,父节点使用shared_ptr包含子节点,子节点使用weak_ptr包含父节点一样
额外
weak_ptr内部保存和shared_ptr相同的block ptr