c++ weak_ptr源代码分析(from visual studio 2017)

  • weak_ptr是一种持有被shared_ptr管理者的资源的弱引用的智能指针。它必须通过转化为shared_ptr来访问管理的资源。
  • weak_ptr被用来跟踪资源,它通过转化为shared_ptr来获取临时所有权。如果这个时候原先拥有资源的shared_ptr销毁了,资源的生命周期将会被延长至这个转化得到的shared_ptr析构之前。
  • weak_ptr另外一个作用是打破shared_ptr可能的循环引用(循环引用会导致应该释放的资源没有释放,此处不展开)。
  • weak_ptr的成员函数等信息见标准:

https://en.cppreference.com/w...

  • weak_ptr的源代码:

58.png

  • weak_ptr是一个模板类,模板参数同shared_ptr一样,为跟踪资源的对应类型。换言之,weak_ptr和shared_ptr拥有同样的数据成员。只不过其成员函数在修改查询引用计数类中不同的数据罢了。

59.png

  • 不知道大家记不记得,引用计数基类中有两个数据成员:_Uses和_Weaks。_Uses则是我们理解的引用计数,每有一个shared_ptr共享资源,_Uses就会加一,反之每一个shared_ptr析构,_Uses就会减一,当_Uses变为零时,意味着没有shared_ptr再占有资源,这个时候占有的资源会调用释放操作。但是并不能直接销毁引用计数对象,因为可能有弱引用还绑定到引用计数对象上。
  • 而_Weaks就是weak_ptr追踪资源的计数器,每有一个weak_ptr追踪资源,_Weaks就会加一,反之每一个weak_ptr析构时,_Weaks就会减一,当_Weaks变为零时,意味着没有weak_ptr再追踪资源,这时会销毁引用计数对象。(注:不会出现_Weaks变为零而_Uses不为零的情况,因为引用计数对象在构造的时候,_Weaks被初始化为1)

60.png

  • 只要使用过weak_ptr,就不会对这两个函数陌生,expired()用来判断是否有对应的shared_ptr占有资源。lock()返回用该weak_ptr构造的shared_ptr对象,我们可以通过这个shared_ptr对象操纵资源。如果在调用lock时,该资源已经被释放,那么返回的shared_ptr将会是empty的(A shared_ptr may also own no objects, in which case it is called empty (an empty shared_ptr may have a non-null stored pointer if the aliasing constructor was used to create it))。
  • lock函数内部先定义了一个空的shared_ptr指针,并调用了其内部函数_Construct_from_weak,该函数的参数是weak_ptr,这里把自己传了进去。

61.png

  • 这个函数内部检查了_Other._Rep是不是一个空指针,如果不是的话,调用_Other._Rep->_Incref_nz()并判断其返回值,如果是true的话,用这个weak_ptr内部的数据成员赋值给本shared_ptr,完成构造并返回true,否则返回false。
  • 可以想象,_Incref_nz()做了以下这些事:
  1. 如果weak_ptr追踪的资源这时候已经被释放,则该函数返回false。
  2. 如果weak_ptr追踪的资源还没有被释放,则其引用计数_Uses自增一,返回true。
  • 如果在单线程条件下,这段代码应该并不难写,但是要保证不同线程中调用的正确性,则需要了解一些多线程编程的技巧。

62.png

  • 这段代码在一个无限循环的条件下,首先获取_Uses的值,(这里需要static_cast转换为volatile _Atomic_counter_t&,为了限制编译器对其进行优化,volatile使得每次读取_Uses时,必须从内存中重新读取,而不是使用保存在寄存器里的备份。)如果这时_Uses已经变为0,则意味着资源已经被释放,返回false。否则我们调用_InterlockedCompareExchange函数来原子的对_Uses加一。
  • _InterlockedCompareExchange是多线程编程中compare_exchange原语的一种实现,该函数有三个参数,第一个参数我们称为当前值,第二个参数称为设定值,第三个参数称为期望值。当当前值等于期望值时,函数将当前值设置为设定值,并返回当前值原先的值。当当前值不等于期望值时,函数不会进行任何操作,只返回当前值的值,整个操作是原子的。
  • 这里涉及到多线程编程中原子变量的知识,大家可以去找相关的书去看,一两句话解释不清楚,总之,这样的操作可以保证多个线程中调用这个函数时,能正确的修改引用计数。而之所以把这段代码放入for(;;)这个无限循环中执行,因为可能有其他线程中的对象也在修改_Uses,故_InterlockedCompareExchange并不保证每次都能执行成功。(例如其他线程在我们获取_Uses的值到调用_InterlockedCompareExchange之前修改了_Uses)
  • 其实weak_ptr的实现很简洁,重点分析的除了lock操作,还有我认为就是和enable_shared_from_this相关的部分了。关于enable_shared_from_this我们放到下一节讲。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章