我有一个结构体,A
,其对象由shared_ptr
s.结构A
包含对结构B
的引用。B
对象需要跟踪哪些A
对象包含对它们的引用,并且还需要能够向这些对象返回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()
,但我不禁认为我只是做错了什么。
不要急切地从B::set_of_a
中删除元素。当您lock
访问对象的weak_ptr
时,请检查它是否为 null,然后擦除。
你不能shared_from_this
~A
开始后,A
已经停止了。
由于您没有提到编译器 - 如果您使用的是足够新的编译器,那么您可以使用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
关于科里鲁(GCC 8)。