在 shared_ptr 过期后定位 weak_ptr

Locating a weak_ptr after shared_ptr is expired

我有一个结构 A,它的对象由 shared_ptr 管理。结构 A 持有对结构 B 的引用。 B 对象需要跟踪哪些 A 对象持有对它们的引用,并且还需要能够 return shared_ptr 到这些对象。为方便起见,我将一组 weak_ptr 存储到 B 内的关联 A 对象。到目前为止,还不错。

我的问题是我希望 A 的析构函数从其关联的 B 对象中删除对自身的引用。但是,(我认为是)明显的解决方案不起作用,因为在调用 A 的析构函数时,其关联的 weak_ptr 已过期,因此很难从集合中删除.这是我尝试过的:

#include <memory>
#include <set>

struct A;
struct B;

struct B{
   std::set<std::weak_ptr<A>, std::owner_less<std::weak_ptr<A>>> set_of_a;
};

struct A : std::enable_shared_from_this<A>{
   B &b;
   A(B &b):b(b){};
   ~A(){
      // bad_weak_ptr exception here
      b.set_of_a.erase(shared_from_this());
   }
};


int main(){
   B b;
   std::shared_ptr<A> a1 = std::make_shared<A>(b);
   b.set_of_a.insert(a1);
   {
      std::shared_ptr<A> a2 = std::make_shared<A>(b);
      b.set_of_a.insert(a2);
   }
   return 0;
}

完成此任务的正确方法是什么?我可以让 A 的析构函数 运行 通过 B 的设置并删除任何过期的 weak_ptrs,但这看起来并不干净。我也可以将 B 的集合转换为原始 A 指针,并在需要时使用这些原始指针访问 A 的 shared_from_this(),但我忍不住想我只是做错事了。

由于您没有提到编译器 - 如果您使用的是足够新的编译器,那么您可以使用 weak_from_this(可从 C++17 获得):

b.set_of_a.erase(weak_from_this());

这实际上会以一种干净的方式实现您想要的,从那时起您只需比较实际的 weak_ptr 实例,而不是尝试在 dtor 中创建一个新的共享实例,这在逻辑上现在失败了。似乎适用于 coliru,但不适用于启用了 C++17 的 VS2017 (15.4.1)。

为好奇的家伙更新

这段代码:

#include <memory>
#include <set>
#include <iostream>

struct A;
struct B;

struct B{
   std::set<std::weak_ptr<A>, std::owner_less<std::weak_ptr<A>>> set_of_a;
};

struct A : std::enable_shared_from_this<A>{
   B &b;
   A(B &b):b(b){};
   ~A(){
      b.set_of_a.erase(weak_from_this());
      std::cout << "Size of set_of_a: " << b.set_of_a.size() << "\n";
   }
};


int main(){
   B b;
   std::shared_ptr<A> a1 = std::make_shared<A>(b);
   b.set_of_a.insert(a1);
   {
      std::shared_ptr<A> a2 = std::make_shared<A>(b);
      b.set_of_a.insert(a2);
   }
   return 0;
}

给出输出:

Size of set_of_a: 1
Size of set_of_a: 0

在 coliru (gcc 8) 上。

不要急切地从 B::set_of_a 中删除元素。当你lock weak_ptr 访问对象时,检查它是否为空,然后擦除。

你不能shared_from_this~A开始之后,A已经不复存在了。