Item20 Use std::weak_ptr for std::shared_ptr like pointers that can dangle

​   原始指针有一个致命的问题就是无法探知生死,尽管你可以通过释放内存后设置为空来解决部分场景下存在的问题,但是这治标不治本,当有多个指针指向同一个内存资源的时候就无能无力了。那么在这篇文章中我将介绍一个大杀器std::weak_ptr,它可以探查生死,探查指针所指向的内存资源是否有效。陈硕有篇文章就专门介绍了std::weak_ptr,讲的很好,本文算是自己对std::weak_ptr的理解。

auto spw = std::make_shared<Widget>();
std::weak_ptr<Widget> wpw(spw);
spw = nullptr
if (wpw.expired()) {
  std::cout << "Widget already destructor" << std::endl;
}

​   在上面的代码中swp = nullptr导致引用计数变为0,触发了Widget的析构,此时使用weak_ptrexpired方法就可以探测这个对象是否已经析构了,这是weak_ptr的典型用法。那么weak_ptr除了严查生死外,能不能解引用来访问对象呢?答案是不,不过它提供了lock方法可以将weak_ptr转换为shared_ptr如下:

if (!wpw.expired()) {
  std::shared_ptr spw2(wpw);    // 提升为shared_ptr,如果wpw过期,仍然要这样初始化会抛出                                  // std::bad_weak_ptr异常
}

​   如果没有过期就进行提升,看似没有问题,但是这两步是非原子的,这就导致了线程安全的问题,不过幸好C++标准库的设计者考虑到了这个问题,提供了一个lock函数可以直接提升为shared_ptr,如果过期了就返回nullptr

​   上面基本都是在说weak_ptr本身,那么接下来谈论是weak_ptr的用途,它是如何用在工程实践中。第一种就是用作缓存,实际工程中我们可能会new很多对象,但是想根据某个key对其缓存,所以通常会将key和对象使用map保存起来。最简单的就是把对象的原始指针保存在map,很显然这不够好,因为不知道这个对象是否死了。如果存放shared_ptr的确是可以保证对象是存活的(因为map中本身就保存了一份,所以引用计数至少是1),那么这会导致另外一个问题就是对象的生存周期被拉长了,和map一样长。但是使用weak_ptr就不会存在这个问题,对象随时都会被回收不会延长对象的生命周期。

std::unordered_map<WidgetID, std::weak_ptr<const Widget> > cache;

​   weak_ptr的第二个用处就是解决循环引用的问题,先开看下面这张图,理解下什么是循环引用。

reference

​   上图中的A和B的关系就是循环引用的关系,当A对象有一个shared_ptr指向B,B对象也有一个shared_ptr指向A,那么两者都持有对方的引用计数,导致对象无法析构。现实工程中存在这样的引用也是在情理之中的,那么使用weak_ptr就可以打破循环引用。又能通过weak_ptr访问对象,以满足工程上的需求。

©️2020 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值