在shared_ptr过期之后找到一个weak_ptr

时间:2018-07-31 21:29:02

标签: c++ shared-ptr smart-pointers weak-ptr

我有一个结构A,其对象由shared_ptr s管理。结构A拥有对结构B的引用。 B个对象需要跟踪哪些A个对象持有对其的引用,并且还需要能够向这些对象返回shared_ptr个。为了简化操作,我将一组weak_ptr存储到A内的关联B对象中。到目前为止,一切都很好。

我的问题是我希望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(),但我忍不住想,我只是做错了事。

2 个答案:

答案 0 :(得分:4)

不要费心地删除B::set_of_a中的元素。 lock weak_ptr访问对象时,检查它是否为空,然后擦除。

shared_from_this开始后,~A不再存在,您无法A

答案 1 :(得分:1)

由于您没有提到编译器-如果您使用的是足够新的编译器,则可以使用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)上。