我有一个结构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()
,但我忍不住想,我只是做错了事。
答案 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)上。