weak_ptr如何知道共享资源已过期

How does a weak_ptr know that the shared resources has expired?

本文关键字:共享资源 过期 何知道 ptr weak      更新时间:2023-10-16

考虑以下代码:

#include <memory>
#include <iostream>
using namespace std;
struct MySharedStruct
{
  int i;
};
void print_value_of_i(weak_ptr<MySharedStruct> weakPtr)
{
  if (shared_ptr<MySharedStruct> sp = weakPtr.lock())
  { cout << "Value of i = " << sp->i << endl; }
  else
  { cout << "Resource has expired"; }
}
int main()
{
  shared_ptr<MySharedStruct> sharedPtr(new MySharedStruct() );
  sharedPtr->i = 5;
  weak_ptr<MySharedStruct> weakPtr;
  weakPtr = sharedPtr;
  print_value_of_i(weakPtr);
  sharedPtr.reset(new MySharedStruct() ); // <<----- How does weak_ptr know it has expired after this line executes?
  sharedPtr->i = 10;
  print_value_of_i(weakPtr);
  return 0;
}

考虑到shared_ptr所引用的资源基本上已被另一个资源取代,weak_ptr如何知道它已过期?weak_ptr会跟踪什么以确保共享资源被销毁并被的共享资源替换?weak_ptr中的方法(例如lock(的示例定义(如果相关的话(将被理解。

从纯指针创建shared_ptr时分配的控制块包含对象的引用计数器、指向对象本身的指针以及自定义deleter对象(如果有的话(。当引用计数器达到零时,对象被释放,指针被设置为null。因此,当对象引用计数器为零时,意味着该对象已不存在。

对于x86和x86-64,它们使用原子操作,没有显式锁定(没有互斥或自旋锁(。实现的诀窍是一个特殊的无锁(繁忙自旋的代码语言(函数atomic_conditional_increment,它只在对象引用计数器不为零的情况下递增对象引用计数器。它用于weak_ptr::lock函数的实现,以应对多个线程试图从对象引用计数器为零的同一weak_ptr创建shared_ptr时的竞争。看见http://www.boost.org/doc/libs/1_52_0/boost/smart_ptr/detail/sp_counted_base_gcc_x86.hpp

控制块本身在shared_ptrweak_ptr之间共享,并且有另一个自己的引用计数器,因此它一直保持活动状态,直到对它的最后一个引用被释放。

当一个shared_ptr被重新分配时,它指向另一个控制块,因此一个控制区块只指向一个相同的对象。换句话说,在控制块中不存在用另一个对象替换一个对象的情况。

简短回答

我怀疑大多数实现都是通过weakPtrsharedPtr都引用的共享控制块来实现这一点的。当sharedPtr重置时,它会减少控制块中的use_countweakPtr可以用来测试指针是否有效。

答案很长

但我认为这可能因实施情况而异。以下是C++11标准所说的应该发生的事情:

shared_ptr<MySharedStruct> sharedPtr(new MySharedStruct());

根据20.7.2.2.1sharedPtr是在拥有给定数据的情况下构建的。

weak_ptr<MySharedStruct> weakPtr;
weakPtr = sharedPtr;

根据20.7.2.3.1,构造weakPtr,然后为其赋值sharedPtr。在分配之后,weakPtrsharedPtr现在共享给定数据的所有权。

sharedPtr.reset(new MySharedStruct());

根据20.7.2.2.4reset(Y*)等效于shared_ptr(Y*).swap(*this)。换句话说,sharedPtr用拥有新数据的临时shared_ptr交换其内容。

交换后,sharedPtr将拥有新数据,临时数据将与weakPtr共享旧数据的所有权。

根据20.7.2.2.2,然后销毁临时:

  • 由于临时拥有旧数据,并且不与另一个shared_ptr实例共享该所有权,因此它会删除旧数据
  • 与临时shared_ptr共享所有权的所有实例都将报告比其先前值少一个的use_count()

这意味着CCD_ 33。

if (shared_ptr<MySharedStruct> sp = weakPtr.lock()) { 
  cout << "Value of i = " << sp->i << endl;
} else {
  cout << "Resource has expired"; 
}

根据20.7.2.3.5,调用lock相当于

expired() ? shared_ptr<T>() : shared_ptr<T>(*this)

CCD_ 35相当于

use_count() == 0

这意味着CCD_ 36将返回一个空的CCD_。