Object A owns an Object B (has a pointer to it)
When Object A is destroyed, Object B is destroyed too.
Object C has a std::vector of pointers to Object B-s.
When Object B is destroyed, remove its pointer from Object C's vector.
对象 A 的生命周期可以由 shared_ptr 管理。
它可以完全控制B 的生命周期:
struct A {
std::unique_ptr<B> b;
};
或
struct A {
B b;
};
我们将添加一个observe_B 方法:
struct A {
std::unique_ptr<B> b;
B* observe_B() { return b.get(); }
B const* observe_B() const { return b.get(); }
};
我们将在逻辑上将其设为 const。对于我们有一个实际的B 的情况,我们只需使用& 而不是.get()。所以我们不再关心B 是如何分配的(指针或在A 的主体中)。
现在我们有一个相对复杂的生命周期请求。在这里明智地使用shared_ptr 可能是合适的。其实shared_from_this:
struct A:std::enable_shared_from_this<A> {
std::unique_ptr<B> b;
B* observe_B() { return b.get(); }
B const* observe_B() const { return b.get(); }
std::shared_ptr<B const> get_shared_B() const {
return {shared_from_this(), observe_B()};
}
std::shared_ptr<B> get_shared_B() {
return {shared_from_this(), observe_B()};
}
};
这里我们使用共享指针的“别名构造函数”来返回一个指向非共享对象的共享指针。它正是为此目的而设计的。我们使用A 的共享生命周期语义,但将其应用于B*。
在C 中,我们只需存储vector<weak_ptr>。
struct C {
std::vector<std::weak_ptr<B>> m_Bs;
};
现在,当A 消失时,weak_ptr 对“包含”B 的最后一个强引用丢失。当你.lock()它时,它现在失败了。
struct C {
std::vector<std::weak_ptr<B>> m_Bs;
void tidy_Bs() {
auto it = std::remove_if( begin(m_Bs), end(m_Bs), [](auto&& x)->bool{return !x.lock();});
m_Bs.erase(it, end(m_Bs));
}
};
tidy_Bs 删除所有“悬空”weak_ptrs 到 B 中的 m_Bs。
要迭代,我通常会这样做:
struct C {
std::vector<std::weak_ptr<B>> m_Bs;
void tidy_Bs() {
auto it = std::remove_if( begin(m_Bs), end(m_Bs), [](auto&& x)->bool{return !x.lock();});
m_Bs.erase(it, end(m_Bs));
}
template<class F>
void foreach_B(F&& f) {
tidy_Bs();
auto tmp = m_Bs;
for (auto ptr:m_Bs)
if (auto locked = ptr.lock())
f(*locked);
}
};
它为m_Bs 向量中每个仍然存在的Bs 传递f 和B&。当它在那里时,它会清理死者。
我复制向量是因为在迭代时,有人可以去更改 m_Bs 的内容,并且为了稳健,我不能在发生这种情况时迭代 m_Bs。
整个技术可以在 A 不被 shared_ptr 管理的情况下完成;但是B 必须由shared_ptr 管理。
请注意,如果 C 当前在 B 包含在 A 中的 .lock() 上,则“通常”会导致 A 被销毁的操作实际上可能不会执行此操作。实际上没有办法避免这种情况,除了让C 崩溃。