问题是 的成员函数B
不是 的成员函数A
,即使B
派生自A
。如果您有void (A::*)()
,则可以在 any 上调用它A *
,而不管指向的对象的实际派生类型如何。
(当然,同样的原则也适用于 a A &
。)
假设B
派生自A
,并C
派生自A
;如果可以将 a void (B::*)()
(say) 视为 a void (A::*)()
,则可以执行以下操作:
A *c=new C;
A *b=new B;
void (A::*f)()=&B::fn;//fn is not defined in A
(c->*f)();
的成员函数B
将在类型的对象上调用C
。结果充其量是不可预测的。
基于示例代码,并假设不使用 boost 之类的东西,我倾向于将回调构造为一个对象:
class Callback {
public:
virtual ~Callback() {
}
virtual Do(int a)=0;
};
然后调用回调的函数以某种方式获取这些对象之一,而不是普通的函数指针:
class A {
protected:
void doSomething(Callback *c) {
c->Do(1234);
}
};
然后,您可以为每个您有兴趣调用的派生函数设置一个回调。以 doIt 为例:
class B:public A {
public:
void runDoIt() {
DoItCallback cb(this);
this->doSomething(&cb);
}
protected:
void doIt(int foo) {
// whatever
}
private:
class DoItCallback:public Callback {
public:
DoItCallback(B *b):b_(b) {}
void Do(int a) {
b_->doIt(a);
}
private:
B *b_;
};
};
减少样板文件的一个明显方法是将成员函数指针放入回调中,因为派生的回调可以自由地处理特定类型的对象。这将使回调更通用一点,因为当回调时,它将调用 B 类型对象上的任意成员函数:
class BCallback:public Callback {
public:
BCallback(B *obj,void (B::*fn)(int)):obj_(obj),fn_(fn) {}
void Do(int a) {
(obj_->*fn_)(a);
}
private:
B *obj_;
void (B::*fn_)(int);
};
这将使 doIt 像这样:
void B::runDoIt() {
BCallback cb(this,&B::doIt);
this->doSomething(&cb);
}
这可能会被“改进”,尽管并非所有读者都可以通过以下方式看到它:
template<class T>
class GenericCallback:public Callback {
public:
GenericCallback(T *obj,void (T::*fn)(int)):obj_(obj),fn_(fn) {}
void Do(int a) {
(obj_->*fn_)(a);
}
private:
T *obj_;
void (T::*fn_)(int);
};
使用它,上面的 runDoIt 函数可以变成:
void B::runDoIt() {
GenericCallback<B> cb(this,&B::doIt);
this->doSomething(&cb);
}
(通用回调也可以在成员函数指针本身上进行模板化,尽管在大多数情况下这不太可能提供任何实际优势。它只是更多的输入。)
我发现以这种方式构建事物效果很好,因为它不需要继承。因此,它对要回调的代码几乎没有限制,这在我看来总是一件好事,而且我发现这超过了难以完全消除的冗长。不可能根据示例来判断这种方法是否真的适合,但是......