【问题标题】:Managing the lifetime of member functions bound by `std::bind`管理由`std::bind`绑定的成员函数的生命周期
【发布时间】:2015-06-28 05:51:42
【问题描述】:

我目前正在尝试用 C++11 编写一个事件队列。我正在使用std::bind 获取std::function 对象,这些对象在某些事件发生时被调用。代码大致如下:

class A
{
public:
  void handle();
};

class B { ... };

// Later on, somewhere else...
std::vector< std::function< void() > functions;

A a;
B b;

functions.push_back( std::bind( &A::handle, &a ) );
functions.push_back( std::bind( &B::handle, &b ) );

// Even later:
for( auto&& f : functions )
  f(); // <--- How do I know whether f is still "valid"?

有什么方法可以保证函数对象的有效性,这样我就可以避免在这里遇到未定义的行为?

我已经在这里查看了这个问题,std::function to member function of object and lifetime of object,但它只讨论了删除指向绑定对象的指针是否会引发未定义的行为。我对如何处理此类对象的破坏更感兴趣。有什么方法可以检测到吗?

编辑:澄清一下,我知道我不能保证非静态、非全局对象的生命周期。通知它们的销毁就足够了,这样可以删除无效的函数对象。

【问题讨论】:

  • 一个成员函数没有“生命周期”,你在绑定成员函数时使用的对象,另一方面,确实有生命周期,你要考虑的是对象的生命周期.不幸的是,没有办法保证非静态和非全局对象的生命周期。
  • 是的,你是对的。是否可以通知非静态、非全局对象的销毁?或者我应该依靠各自的类实例来处理函数对象在删除时的删除?

标签: c++ c++11 stdbind


【解决方案1】:

正如@Joachim 所说,没有生命周期与成员函数相关联(它是代码部分,而不是数据)。所以你问是否有办法在执行回调调用之前知道对象是否仍然存在。

您必须创建一种框架,其中对象 dctor 在销毁时通知容器,因此容器可以将其从其“观察者”中删除,该向量包含所有对象。为此,对象必须在其实例中记住指向容器的 ptr。

更新

@Jason 谈到了 shared_ptr 的使用。可以使用它们,但在这种情况下,没有解决如何销毁在其他对象通知列表中链接的对象的情况。 Shared_ptr 推迟实例的销毁,直到删除所有对它的“托管”引用。但是,如果您需要销毁对象 A,并删除对它的所有引用,因为必须删除该对象,您必须查看所有存储 shared_ptr 的容器并将其删除。一个非常痛苦的活动。最简单的解决方案(使用 raw ptr 或 shared_ptr,如果你可以使用它们,则无关紧要)是观察者和被观察者之间的双链路连接,这样每个人都可以将其销毁通知给另一个人。如何存储这些信息?实现它的方法很多:哈希表、观察者中的插槽等

【讨论】:

  • 感谢您的回答。我现在将使用辅助对象来管理连接。我只是认为值得讨论潜在的替代方案,因为我的解决方案并不优雅。
【解决方案2】:

实现所需结果的一种技巧/解决方法是使用 std::shared_ptr 类型的参数。当绑定被破坏时,共享指针也会被破坏——当它是最后一个引用时,它会做正确的事情。然而,这涉及到对所用签名的更改。为了让它稍微不那么尴尬,你可以使用接受 std::shared_ptr 的静态方法 - 有点像 python 中的 self 参数概念,如果你熟悉的话。

或者如果你对 C++11 没问题,你可以只使用共享指针的 lambda 捕获。

您需要动态分配实例才能使用此方法。

【讨论】:

  • 这不是问的问题:如果你放了一个shared_ptr,你就没有解决对象被破坏的情况;该对象没有被破坏,并且“可观察”仍然发出通知。您必须手动迭代并删除对删除对象的所有引用,这是非常不切实际的。
  • 你能解释一下 lambda 捕获吗?我不确定你的意思。
  • @Mouze - 对于这些类型的对象,您必须一直使用 shared_ptr。当存在有效的 shared_ptr 实例时,无法销毁该对象。在示例中,当函数列表删除带有 std::bind 结果的 std::function,并且不存在其他 shared_ptr 时,只有这样才会删除该对象。
  • @Gnosophilon:您仍然必须在 lambda 方法中使用 shared_ptrs,但它使您不必使用将“this”作为参数的方法:[shared_this](){ shared_this-&gt;handle(); } 还要注意 std::enable_shared_from_this
  • @Mouze op 的问题暂时发生了一些变化。我读到的原始(并且仍然有效)问题是保证回调始终可以使用有效对象执行,并且 shared_ptr 解决了该问题,因为函数对象的容器现在使这些对象保持活动状态。管理注册回调的容器是一个不同的问题,可以通过多种方式解决,例如“注册”返回的链表中的票证迭代器。 Boost.Asio 定期演示 shared_this 模式与一个非常相似的问题,在他们的异步教程中达到顶峰。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-14
  • 2015-08-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多