【问题标题】:Safe callback design安全回调设计
【发布时间】:2013-09-10 17:42:27
【问题描述】:

我有一个回调类,在整个应用程序的某些地方使用。它是这样构造的:

struct A {
   void f();
}

A * callee = new A;
Callback_0 callback = CCallback_0(callee, &A::f);

callback 现在可以存储并在以后调用。这样做的问题是,如果 callee 在回调创建和回调调用之间被破坏,程序就会崩溃(或更糟)。

目前我能想到的唯一解决方案:任何想要成为被调用者的类都必须从基类Callee 继承:

class Callee {
public:
   void registerCallback(const Callback& c) {
      _callbacks.push_back(c);
   }
   ~Callee() {
      std::for_each(_callbacks.begin(), _callbacks.end(), [](Callback& c){c.disable();});
   }

private:
   std::vector<Callback> _callbacks;
}

这种设计的缺点是我必须继承每个可能需要调用的类。有没有更好的办法? 我更喜欢 C++03 解决方案,我只有有限的 C++11 支持(受 MSVC2010 和 Mac 上的 clang 限制)。

【问题讨论】:

  • 您可以要求被调用者由shared_ptr 所有权方案管理。
  • 传递一个非 const 引用并将其留给用户以确保对象的生命周期怎么样?
  • @DieterLücking:我是用户。在某些情况下,我无法避免我所描述的崩溃。

标签: c++ callback


【解决方案1】:

如果我们认为您面临的一般问题是,您正在对两个独立对象的创建和删除活动进行排序,并且在这些状态之间(C &amp; D),您希望它们之间有关联。

这对于callback 类设计不是很具体,而且对于任何此类问题区域都非常通用。 callback 类设计只是整个问题集的一个子类——在我们的系统设计中以不同的命名法再次出现。

AFAI,只有两种方法可以处理此类设计问题 -
一个。将创建/销毁的所有权授予调用者对象。您可以通过将被调用者的属性传递给回调并将被调用者的生命周期的责任交给调用者来实现这一点。这可以确保 - 对象在操作过程中不会被意外删除 - 例如当前设计中的callback

b.另一种方法是使用shared_ptrobject counter mechasim(如评论中所述),这将确保只有在use-up 的最后一个实例之后才会释放被调用者。

当然,这些都是技术上的方法,现在要考虑它如何适合我们的设计并对其有意义。从这个角度来看,Option b 似乎比Option a 更有意义。

也就是说,根据我的经验,我没有看到太多使用a or b 选项的代码。我们只是让calleecaller 保持独立,并确保callee 不会被仔细编码破坏:)

【讨论】:

    【解决方案2】:

    第一个解决方案是通过类本身来管理所有调用者。这可以通过从您已经描述的某种管理器类派生来完成。

    第二个是设置一些拥有所有可调用实例和调用者列表的管理器类。实例必须由这个管理器类创建和删除,所有注册的回调必须由这个管理器实例拥有。

    下一个可能是某种 smart/auto_ptr 解决方案。每个指针都连接到某种取消注册回调的管理器类。

    基本上,您必须知道实例何时死亡,而您想如何执行此操作并不重要。

    对我来说,这听起来像是一个基本的设计问题。外部世界必须知道任何类的一些(可能是未知的)实例的存在,并且没有明确定义的接口来在不同的类和它们的实例之间进行交互。也许代理可以帮助管理界面。但是,如果不了解您的程序的其余部分,我们将找不到最佳解决方案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-18
      • 2012-05-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多