安全回调设计

时间:2013-09-10 09:13:40

标签: c++ callback

我有一个回调类,在我的应用程序中的某些地方使用。它的构造如下:

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限制)。

2 个答案:

答案 0 :(得分:1)

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

这对于callback类设计并不是非常具体,它对于任何此类问题领域都是非常通用的。 callback类设计只是整个问题集的一个子类 - 它在我们的系统设计中以不同的命名法重新出现。

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

湾其他方法是拥有shared_ptrobject counter mechasim(如评论中所述),这将确保仅在use-up的最后一个实例之后释放被调用者。

当然,这些都是技术方面的,现在我们如何考虑它的设计和意义。从这个角度来看,Option b似乎比Option a更有意义。

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

答案 1 :(得分:0)

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

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

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

基本上你必须知道一个实例何时死亡,你想要怎么做并不重要。

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