# 深入理解 C++ weak_ptr
面试高频指数:★★☆☆☆
# weak_ptr 是什么?
std::weak_ptr
是C++11引入的一种智能指针,主要与std::shared_ptr
配合使用。
它的主要作用是解决循环引用问题、观察std::shared_ptr
对象而不影响引用计数,以及在需要时提供对底层资源的访问。
- 解决循环引用问题:当两个或多个
std::shared_ptr
对象互相引用时,会导致循环引用。这种情况下,这些对象的引用计数永远不会变为0,从而导致内存泄漏。
std::weak_ptr
可以打破这种循环引用,因为它不会增加引用计数。只需要将其中一个对象的std::shared_ptr
替换为std::weak_ptr
,即可解决循环引用问题。
- 观察
std::shared_ptr
对象:std::weak_ptr
可以用作观察者,监视std::shared_ptr
对象的生命周期。它不会增加引用计数,因此不会影响资源的释放。
# 深入理解weak_ptr: 资源所有权问题
看到一篇对于 weak_ptr 讲解非常棒的文章,转到这里分享给大家:
原文链接: https://0cch.com/2022/10/31/some-tips-about-weakptr/ 作者:0cch
虽然智能指针进入C++11标准库已经有十多年了,但是我们对部分细节的理解还是比较局限。
以std::weak_ptr
为例,很多人的理解只是停留在避免std::shared_ptr
出现相互引用,导致对象无法析构,内存无法释放的问题。
当然,并不是说这种用法有什么不对,恰恰相反,它是一个非常经典的使用场景。
但是std::weak_ptr
的使用场景或者说它诞生的理念却不仅仅是这些,如果没有更加透彻理解std::weak_ptr
,也很难合理的使用std::shared_ptr
。
std::weak_ptr
从概念上,它是一个智能指针,相对于std::shared_ptr
,它对于引用的对象是“弱引用”的关系。
简单来说,它并不“拥有”对象本身。
如果我们去类比生活中的场景,那么它可以是一个房地产中介。房地产中介并不拥有房子,但是我们有办法找到注册过的房产资源。
在客户想要买房子的时候,它起初并不知道房子是否已经卖出了,它需要找到房主询问后再答复客户。
std::weak_ptr
做的事情几乎和房产中介是一模一样的。std::weak_ptr
并不拥有对象,在另外一个std::shared_ptr
想要拥有对象的时候,它并不能做决定,需要转化到一个std::shared_ptr
后才能使用对象。所以std::weak_ptr
只是一个“引路人”而已。
说了这么多,那么std::weak_ptr
除了解决相互引用的问题,还能做什么?
答案是:一切应该不具有对象所有权,又想安全访问对象的情况。
还是以互相引用的情况为例,通常的场景是:一个公司类可以拥有员工,那么这些员工就使用std::shared_ptr
维护。另外有时候我们希望员工也能找到他的公司,所以也是用std::shared_ptr
维护,这个时候问题就出来了。但是实际情况是,员工并不拥有公司,所以应该用std::weak_ptr
来维护对公司的指针。
再举一个例子:我们要使用异步方式执行一系列的Task,并且Task执行完毕后获取最后的结果。所以发起Task的一方和异步执行Task的一方都需要拥有Task。
但是有时候,我们还想去了解一个Task的执行状态,比如每10秒看看进度如何,这种时候也许我们会将Task放到一个链表中做监控。这里需要注意的是,这个监控链表并不应该拥有Task本身,放到链表中的Task的生命周期不应该被一个观察者修改。所以这个时候就需要用到std::weak_ptr
来安全的访问Task对象了。
最后再来聊一个新手使用std::weak_ptr
容易被坑的地方:对象资源竞争。
以下代码在多线程程序中是存在很大风险的,因为wp.expired()
和wp.lock()
运行的期间对象可能被释放:
/ std::weak_ptr<SomeClass> wp{ sp };
if (!wp.expired()) {
wp.lock()->DoSomething();
}
正确的做法是:
auto sp = wp.lock();
if (sp) {
sp->DoSomething();
}
std::weak_ptr
的lock
函数是一个原子操作。有趣的是,最开始的C++11标准是没有提到原子操作的,C++14标准才对这一点进行了补充,详细过程可以参考提案文档:LWG2316 (opens new window)。
最新原创的文章都先发布在公众号,欢迎关注哦~,
扫描下方二维码回复「CS」可以获得我汇总整理的计算机学习资料~