您的位置:首页 > Web前端

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

2016-11-23 22:37 465 查看
​   原始指针有一个致命的问题就是无法探知生死,尽管你可以通过释放内存后设置为空来解决部分场景下存在的问题,但是这治标不治本,当有多个指针指向同一个内存资源的时候就无能无力了。那么在这篇文章中我将介绍一个大杀器
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_ptr
expired
方法就可以探测这个对象是否已经析构了,这是
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
的第二个用处就是解决循环引用的问题,先开看下面这张图,理解下什么是循环引用。



​   上图中的A和B的关系就是循环引用的关系,当A对象有一个shared_ptr指向B,B对象也有一个shared_ptr指向A,那么两者都持有对方的引用计数,导致对象无法析构。现实工程中存在这样的引用也是在情理之中的,那么使用weak_ptr就可以打破循环引用。又能通过weak_ptr访问对象,以满足工程上的需求。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐