49

没有存在感的weak_ptr

 4 years ago
source link: https://www.tuicool.com/articles/3quQZvN
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

C++11标准中,引入了三种智能指针:其中shared_ptr、unique_ptr露面的机会非常多,大家也越来越接受使用shared_ptr、unique_ptr来取代普通指针,省去了手动管理内存的烦恼。但是,作为智能指针三胞胎之一的weak_ptr,正如其名,存在感真是相当弱。面试中也经常发现,应聘者往往能够对shared_ptr、unique_ptr如数家珍,但是说到weak_ptr就经常语塞不知所谓了。

其实,weak_ptr的引入是为了解决老大哥shared_ptr存在的一个问题。shared_ptr相比于前辈auto_ptr虽然已经完善了不少,但是由于其基于引用计数来管理内存,导致shared_ptr在存在循环引用的情况下,会存在内存泄露!

如下图所示(高能预警,灵魂画手上线):

jyeYjq6.jpg!web

obj A和B均是通过shared_ptr持有的对象,同时obj A内部有一个shared_ptr指向obj B,同时obj B内部也有一个shared_ptr指向obj A。那么,obj A和obj B的引用计数将同时为2。当外部指向A和B的shared_ptr离开作用域时,引用计数各自减1,这时obj A和obj B均不会被析构,但是外部的shared_ptr已经没有了,也就是这两个对象成了孤立对象,内存泄露了~

Python也是使用引用计数作为垃圾回收的主要方式的,那Python是怎么处理的呢?

Python在引用计数之外,又引入了标记-清除(mark-sweep)法作为辅助的垃圾收集机制,专门用于对付这种循环引用导致的孤立对象。

但是,C++并没有GC,也无从找到root objects,标记清除法在智能指针场景下并不适用。

于是,没啥存在感的weak_ptr登场了~

weak_ptr只能从shared_ptr复制而来,并且不会新增shared_ptr的引用计数;weak_ptr销毁时,也不会影响到持有对象的生命周期;更重要的,weak_ptr完全没有重载operator *和operator ->,这样就完全不能直接使用weak_ptr,而只能通过weak_ptr.lock()来获得一个“临时的”shared_ptr对象(此时引用计数加1,但是因为是临时的,所以最后会减1的,放心)。

那么,使用weak_ptr的情况下,上面的图变成了:

e6vuMzV.jpg!web

这时,当外部的shared_ptr离开作用域销毁时,obj A的引用计数减到0,进行析构。析构导致obj A持有的obj B的shared_ptr引用计数减1,同时叠加外部shared_ptr销毁带来的引用计数减1,obj B的引用计数也将从2减为0,从而进行析构。

bingo~内存泄漏没有了~

总结

weak_ptr虽然弱,虽然没有存在感,但是,有时候还是不得不用~

留意一点:weak_ptr只能解决那些程序员明确知道会存在循环引用的情况;如果类之间的引用关系过于复杂,导致“意外地”引入了循环引用,那么weak_ptr也是没辙的,毕竟他不是GC(GC中标记-清除法就可以处理这种情况)。

转载请注明出处: http://blog.guoyb.com/2019/06/15/weak-ptr/

欢迎使用微信扫描下方二维码,关注我的微信公众号TechTalking,技术·生活·思考:

RZbqqiM.jpg!web

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK