C++ 智能指针是否能够从容器中删除对其对象的其他引用?

C++ 智能指针是否能够从容器中删除对其对象的其他引用?,c++,c++11,pointers,c++14,smart-pointers,C++,C++11,Pointers,C++14,Smart Pointers,我想落实这一点: 对象A拥有一个对象B(有一个指向它的指针) 当对象A被销毁时,对象B也被销毁 对象C有一个指向对象B-s的指针的std::vector。 当对象B被销毁时,将其指针从对象C的向量中移除 通过不同智能指针的组合,这可能吗?让我们从角色的角度考虑(现在忽略线程): 对象A拥有B的生存期 对象C是B生命周期的观察者 您没有说明a和C之间是否存在关系,因此我假设a在其构造函数的点上意识到C的参与(让我们使用C作为可配置工厂) B的生命周期事件可以在两个地方创建观察结果——B

我想落实这一点:

  • 对象A拥有一个对象B(有一个指向它的指针)
    • 当对象A被销毁时,对象B也被销毁
  • 对象C有一个指向对象B-s的指针的std::vector。
    • 当对象B被销毁时,将其指针从对象C的向量中移除

通过不同智能指针的组合,这可能吗?

让我们从角色的角度考虑(现在忽略线程):

对象A拥有B的生存期

对象C是B生命周期的观察者

您没有说明a和C之间是否存在关系,因此我假设a在其构造函数的点上意识到C的参与(让我们使用C作为可配置工厂)

B的生命周期事件可以在两个地方创建观察结果——B的构造函数/析构函数(坏-紧耦合)或中间工厂(更好-松耦合)

因此:

#包括
#包括
结构B{
};
结构C{
std::共享_ptr make_b(){
自动p=std::shared_ptr(新B(),[this](B*p){
此->移除观察者(p);
删除p;
});
添加观察者(p.get());
返回p;
}
私人:
无效添加观察员(B*p){
观察者向后推(p);
}
无效移除_观察员(B*p){
观察者擦除(std::remove(std::begin(观察者),std::end(观察者),p),
标准::结束(观察员);
}
std::向量观测器;
};
结构A{
A(C&factory)
:b_(factory.make_b()){}
std::共享的;
};
int main(){
//注:工厂寿命必须超过a1和a2
C工厂;
a1(工厂);
a2(工厂);
}
请注意,虽然我使用了
共享的ptr
,但在这种情况下,我可以轻松地使用
唯一的ptr
。然而,我会在指针中将A与deleter类型耦合——因此我要么创建自己的类型擦除deleter类型,要么将A与C更紧密地耦合(这是我想要避免的)

对象A的生存期可由
共享\u ptr
管理

它可以完全控制
B
的生命周期:

struct A {
  std::unique_ptr<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(); }
};
在这里,我们使用共享指针的“别名构造函数”来返回指向非共享对象的共享指针。正是为了这个目的。我们使用
A
的共享生存期语义,但将其应用于A
B*

C
中,我们只需存储一个
向量

tidy_-Bs
删除
m_-Bs
中所有“悬挂”到
B
弱_-ptr
s

为了迭代,我通常会这样做:

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);
  }
};
struct C{
std::向量m_-Bs;
void tidy_Bs(){
auto it=std::remove_if(begin(mubs)、end(mubs),[](auto&&x)->bool{return!x.lock();});
擦除(它,结束(mubs));
}
模板
无效(F&F){
整洁的;
自动tmp=m_Bs;
用于(自动ptr:m_Bs)
如果(自动锁定=ptr.lock())
f(*锁定);
}
};
它为
m_Bs
向量中每个仍然存在的
B
s传递
f
a
B&
。当它在里面的时候,它会清理死的

我复制向量是因为在迭代时,有人可以去更改
mubs
的内容,为了保持健壮性,我不能在发生这种情况时迭代
mubs

整个技术不需要由
shared\u ptr
管理
A
;但是
B
必须由
shared\u ptr
管理


请注意,如果
C
当前在
A
中包含的
B
上有一个
.lock()
,则“通常”会导致
A
被销毁的操作实际上可能不会这样做。实际上,除了使
C
崩溃之外,没有其他方法可以避免这种情况。

这对你有用吗?(虽然不能完全满足您的要求,但这是解决此类问题的标准方法)类似于
struct A{std::unique_ptr b;}
结构C{std::vector bs;}?这意味着
对象B
或其智能指针必须对包含指向它的(智能?)指针的每个向量具有某种引用。这听起来不太实用。@Jarod42我就是这么想的,但我不知道如果我有一个无限循环,在其中调用C的B-s上的一个方法,那么在每个循环中检查弱锁()而不是在B被破坏时只发信号给C,不是有点无效吗?这种弱指针存储非常安全,但我不知道它是否足够有效,例如对于游戏引擎。@Tudvari:一种可能的方法是将
B
标记为已删除,然后让
C
从其向量中删除并移除对象。。。或者创建一个。
struct A {
  B b;
};
struct A {
  std::unique_ptr<B> b;
  B* observe_B() { return b.get(); }
  B const* observe_B() const { return b.get(); }
};
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()};
  }
};
struct C {
  std::vector<std::weak_ptr<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));
  }
};
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);
  }
};