問題は、 のメンバー関数が のメンバー関数でB
はないことです。A
たとえB
から派生したとしてもですA
。がある場合は、指しているオブジェクトの実際の派生型に関係なく、void (A::*)()
任意の で呼び出すことができます。A *
(もちろん、同じ原則がA &
too にも適用されます。)
B
から派生しA
、からC
派生すると仮定しA
ます。void (B::*)()
(たとえば) 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);
}
};
その後、呼び出したい派生関数ごとに 1 つのコールバックを持つことができます。たとえば、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);
}
(一般的なコールバックは、メンバー関数ポインター自体でテンプレート化することもできますが、これはほとんどの場合、実用的な利点を提供する可能性はほとんどありません。入力が増えるだけです。)
継承を必要としないため、この方法で物事を構造化するとうまくいくことがわかりました。したがって、コールバックされるコードにほとんど制限が課せられません。これは常に良いことだと思います。完全に排除するのが難しい冗長性よりも、それの方が重要であることがわかりました。この例に基づいて、このアプローチが実際に適しているかどうかを判断することは不可能ですが...