我想在c ++中实现一个具有回调的类。
所以我认为我需要一个有两个参数的方法:
条件是:
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似乎是一个好主意,(假设已安装)
答案 0 :(得分:16)
最佳解决方案,将boost::function
与boost::bind
一起使用,或者如果编译器支持tr1 / c ++ 0x,请使用std::tr1::function
和std::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 Pattern和signals and slots。这扩展到多个订阅者。
答案 5 :(得分:2)
在C ++中,很难使用指向类方法的指针。你打电话的事实 - 它是代表,不推荐使用它们。您必须使用虚函数和抽象类,而不是它们。 但是,如果不支持完全不同的编程概念,C ++就不会那么喜欢我。如果你仍然需要委托,你应该看看“boost functional”(C + + 0 x的一部分),它允许指向类的方法而不管类名。此外,在C ++ Builder中有类型__closure - 在编译器级别实现委托。
P.S。抱歉英语不好......