在C++; 我有一个用C++编写的自定义菜单类。为了将代码分成易于阅读的函数,我使用回调

在C++; 我有一个用C++编写的自定义菜单类。为了将代码分成易于阅读的函数,我使用回调,c++,coding-style,callback,C++,Coding Style,Callback,因为我不想在菜单的主机上使用单例,所以我提供了另一个参数(target),它将作为第一个参数提供给回调(某种解决缺少“this”引用的方法) 注册签名 AddItem(string s, void(*callback)(void*,MenuItem*), void* target = NULL) 注册示例 menu->AddItem(TRANSLATE, "translate", &MyApp::OnModeSelected); 处理程序的示例 /* static */ voi

因为我不想在菜单的主机上使用单例,所以我提供了另一个参数(target),它将作为第一个参数提供给回调(某种解决缺少“this”引用的方法)

注册签名

AddItem(string s, void(*callback)(void*,MenuItem*), void* target = NULL)
注册示例

menu->AddItem(TRANSLATE, "translate", &MyApp::OnModeSelected);
处理程序的示例

/* static */
void MyApp::OnModeSelected(void* that, MenuItem* item) {
    MyApp *self = (MyApp*)that;
    self->activeMode = item->text;
}

有没有什么可以用这种方法来考虑的?有更好的吗?

我喜欢你的方法。另一种方法是声明一个接口,从某种意义上说,它是回调的“OO等价物”:

class IMenuEntry {
public:
    virtual void OnMenuEntrySelected(MenuItem* item) = 0;
};
typedef boost::function<void (MenuItem*)> callback_type;
AddItem(const std::string& s, const callback_type& callback = callback_type());
menu->AddItem("Open", boost::bind(&MyClass::Open, this));
注册签名将成为

AddItem(string s, IMenuEntry * entry);
以及该方法的实现

class MyApp : public IMenuEntry {
public:
    virtual void OnMenuEntrySelected(MenuItem* item){
        activeMode = item->text;
    }
}

接口方法将允许您避免丢失的
指针的“void*解决方法”。

除了函数指针签名难以读取之外,我没有发现任何错误。但是,我可能会使用模式来实现这一点。

您可以看看如何使用


我强烈建议你看一下这个。学习它将使您的函数绑定更加容易。

您的方法要求回调函数要么是自由函数,要么是类的静态成员。它不允许客户端将成员函数用作回调。解决此问题的一个方法是将以下内容用作回调的类型:

class IMenuEntry {
public:
    virtual void OnMenuEntrySelected(MenuItem* item) = 0;
};
typedef boost::function<void (MenuItem*)> callback_type;
AddItem(const std::string& s, const callback_type& callback = callback_type());
menu->AddItem("Open", boost::bind(&MyClass::Open, this));

另一个选项是使用,它允许多个回调为同一事件注册。

阅读白皮书。它通过详细分析性能、可用性和其他权衡,为回调机制构建了各种技术。我觉得这本书读起来很难:-(

您可以使用a来封装回调。这将允许您使用C风格的函数或对象接口来提供回调。

另外,不要忘记boost::lambda。它有时可以让您完全不需要回调就可以离开!boost::signals更适合此任务。我可以使用
菜单项&
,但在其他方面完全是一个回调函数贪婪。不应将类的静态成员用作常规回调(它们没有定义的ABI)。boost::函数只是声明接口的泛化(boost::函数只使用接口运算符()。在这种情况下,我希望我的接口更加明确,这样您就不会像Orsogufoy所描述的那样将不适当的方法传回(即让编译器验证回调)。您不应该在此处使用静态成员方法作为回调。您应该只使用声明为extern“C”的函数。您只是碰巧幸运地发现您正在使用的编译器(当前)使用相同的方法来调用静态方法和函数。这不是标准所保证的。