如何在C ++中实现回调?

时间:2010-08-01 11:28:31

标签: c++ callback

我想在c ++中实现一个具有回调的类。

所以我认为我需要一个有两个参数的方法:

  • 目标对象。 (让我们说吧 * MyObj中)
  • 指向成员函数的指针 目标对象。 (所以我能做到 * myObj-> memberFunc(); )

条件是:

  • myObj可以来自任何一个班级。

  • 将成为回调函数的成员函数是非静态的。

我一直在读这个,但看起来我需要事先了解myObj的类。但我不知道该怎么做。我怎么处理这个?这在C ++中是否可行?

这是我想到的,但肯定是不正确的。

class MyClassWithCallback{
public
    void *targetObj;
    void (*callback)(int number);
    void setCallback(void *myObj, void(*callbackPtr)(int number)){
        targetObj = myObj;
        callback = callbackPtr;
    };
    void callCallback(int a){
        (myObj)->ptr(a);
    };
};
class Target{
public
    int res;
    void doSomething(int a){//so something here. This is gonna be the callback function};        
};

int main(){
    Target myTarget;
    MyClassWithCallback myCaller;
    myCaller.setCallback((void *)&myTarget, &doSomething);

}

我感谢任何帮助。

谢谢。

UPDATE 你们大多数人都说观察和代表团,这就是我正在寻找的东西,我是一个有目标C / Cocoa的人。 我目前的实现是使用具有虚函数的接口。我只是认为传递对象和成员函数指针(如boost!)而不是定义接口会“更聪明”。但似乎每个人都认为Interfaces是最简单的方法吗? Boost似乎是一个好主意,(假设已安装)

6 个答案:

答案 0 :(得分:16)

最佳解决方案,将boost::functionboost::bind一起使用,或者如果编译器支持tr1 / c ++ 0x,请使用std::tr1::functionstd::tr1::bind

所以它变得如此简单:

boost::function<void()> callback;
Target myTarget;
callback=boost::bind(&Target::doSomething,&myTarget);

callback(); // calls the function

你的回调变为:

class MyClassWithCallback{
public:
  void setCallback(boost::function<void()> const &cb)
  {
     callback_ = cb;
  }
  void call_it() { callback_(); }
private:
  boost::function<void()> callback_;
};

否则你需要实现一些抽象类

struct callback { 
 virtual void call() = 0;
 virtual ~callback() {}
};

struct TargetCallback {
 virtual void call() { ((*self).*member)()); }
 void (Target::*member)();
 Target *self;
 TargetCallback(void (Target::*m)(),Target *p) : 
       member(m),
       self(p)
 {}
};

然后使用:

myCaller.setCallback(new TargetCallback(&Target::doSomething,&myTarget));

当您的课程被修改为:

class MyClassWithCallback{
public:
  void setCallback(callback *cb)
  {
     callback_.reset(cb);
  }
  void call_it() { callback_->call(); }
private:
  std::auto_ptr<callback> callback_;
};

当然,如果您要调用的函数没有改变,您可能只是实现了一些接口,即通过此调用从某个抽象类派生Target。

答案 1 :(得分:8)

一个技巧是使用接口,如果传入的对象实现了接口,那么您不需要专门知道“MyClassWithCallback”中的类。

e.g。 (伪代码)

struct myinterface
{
  void doSomething()=0;
};

class Target : public myinterface { ..implement doSomething... };

myinterface *targetObj; 
void setCallback(myinterface *myObj){
    targetObj = myObj;
};

进行回调

targetObj->doSomething();

设置:

Target myTarget;
MyClassWithCallback myCaller;
myCaller.setCallback(myTarget);

答案 2 :(得分:5)

Observer设计模式似乎正是您所寻求的。

答案 3 :(得分:4)

您有几个基本选项:

1)指定回调将使用的类,以便对象指针和成员函数指针类型是已知的,并且可以在调用者中使用。该类可能有几个具有相同签名的成员函数,您可以在其中进行选择,但您的选项非常有限。

你在代码中做错了一件事是C ++中的成员函数指针和自由函数指针不一样,并且不是兼容的类型。你的回调注册函数接受一个函数指针,但你试图传递一个成员函数指针。不允许。此外,“this”对象的类型是成员函数指针类型的一部分,因此在C ++中没有“指向任何采用整数并返回void的成员函数的指针”。它必须是“指向Target 的任何成员函数的指针,它接受一个整数并返回void”。因此有限的选择。

2)在接口类中定义纯虚函数。因此,任何想要接收回调的类都可以从接口类继承。由于多重继承,这不会干扰您的类层次结构的其余部分。这与在Java中定义接口几乎完全相同。

3)使用非成员函数进行回调。对于想要使用它的每个类,你编写一个小的stub自由函数,它接受对象指针并在其上调用正确的成员函数。所以在你的情况下你会有:

dosomething_stub(void *obj, int a) {
    ((Target *)obj)->doSomething(a);
}

4)使用模板:

template<typename CB> class MyClassWithCallback {
    CB *callback;
 public:
    void setCallback(CB &cb) { callback = &cb; }
    void callCallback(int a) {
        callback(a);
    }
};

class Target {
    void operator()(int a) { /* do something; */ }
};

int main() {
    Target t;
    MyClassWithCallback<T> caller;
    caller.setCallback(t);
}

您是否可以使用模板取决于您的ClassWithCallback是否是某个大型旧框架的一部分 - 如果是,那么它可能是不可能的(确切地说:可能需要更多技巧,例如继承自非模板类的模板类具有虚拟成员函数的模板类),因为您不一定能为每个回调接收者实例化整个框架一次。

答案 4 :(得分:3)

另外,请查看Observer Patternsignals and slots。这扩展到多个订阅者。

答案 5 :(得分:2)

在C ++中,很难使用指向类方法的指针。你打电话的事实 - 它是代表,不推荐使用它们。您必须使用虚函数和抽象类,而不是它们。 但是,如果不支持完全不同的编程概念,C ++就不会那么喜欢我。如果你仍然需要委托,你应该看看“boost functional”(C + + 0 x的一部分),它允许指向类的方法而不管类名。此外,在C ++ Builder中有类型__closure - 在编译器级别实现委托。

P.S。抱歉英语不好......