関数ポインタを使用した仮想メンバー関数の基本クラス定義の呼び出し
-
05-07-2019 - |
質問
メンバー関数ポインターを使用して、仮想関数の基本クラス実装を呼び出したいと考えています。
class Base {
public:
virtual void func() { cout << "base" << endl; }
};
class Derived: public Base {
public:
void func() { cout << "derived" << endl; }
void callFunc()
{
void (Base::*fp)() = &Base::func;
(this->*fp)(); // Derived::func will be called.
// In my application I store the pointer for later use,
// so I can't simply do Base::func().
}
};
上記のコードでは、 func の派生クラス実装が callFunc から呼び出されます。Base::func を指すメンバー関数ポインターを保存する方法はありますか、それとも使用する必要がありますか? using
何らかの方法で?
実際のアプリケーションでは、boost::bind を使用して callFunc 内に boost::function オブジェクトを作成し、後でプログラムの別の部分から func を呼び出すために使用します。したがって、boost::bind または boost::function にこの問題を回避する何らかの方法があれば、それも役立つでしょう。
解決
参照またはポインタを介して仮想メソッドを呼び出すと、常に最も派生した型を見つける仮想呼び出しメカニズムがアクティブになります。
最善の策は、仮想ではない代替機能を追加することです。
他のヒント
残念ながらあなたがしようとしていることは不可能です。メンバー関数へのポインターは、ポイント先の関数の仮想性を維持するように設計されています。
あなたの問題は、メンバー関数ポインターが裸の関数ポインターとまったく同じではないことです。実際には単なるポインタではなく、 かなり複雑な構造, 、その詳細はコンパイラの実装レベルで異なります。構文経由で呼び出す場合 (this->*fp)()
実際には元のオブジェクトに対して呼び出しているため、仮想関数のディスパッチが発生します。
うまくいくかもしれないことの 1 つは、それを非メソッド ポインター型にキャストすることです。これは少しきしみますが、 考える それは機能するはずです。まだ渡す必要があります Base *
ただし、これを明示的に行うと、仮想関数のディスパッチがバイパスされます。
typedef void BasePointer(Base*);
void callFunc()
{
BasePointer fp = (BasePointer *)&Base::func;
fp(this);
}
アップデート: いいえ、そのようにはできません。それは違法ですし、合法だったとしても安全ではありません。の C++ よくある質問 もっている これについてさらに詳しく. 。しかし、それを知っても問題は解決しません。問題は、呼び出したい場合にオブジェクトへのポインタまたはメンバーへのポインタを使用することです。 Base::func
を通して Base
ポインター、それが指すオブジェクトは、 また になる Base
. 。それをアレンジできれば、メンバー関数ポインターを使用できます。
ここでもう 1 つの考えを紹介します。きれいではありませんが、少なくとも実行可能です。で機能を提供します Derived
, 、非仮想、明示的に呼び出します Base::func
. 。代わりにそれを指してください。多くの異なるバリアントの一般的なケースでこれを行う必要がある場合、これは拡張できません。 func
そして callFunc
しかし、ある方法ではうまくいきます。
関数ポインターを介してこれを行う特定の理由はありますか?
次のように書くことができるはずです:
Base::func();
基本クラスの実装を呼び出します。
クォークの言うことに加えて、より一般的な発言は、裸の関数ポインタではなく、シグナル/スロットの実装を使用する必要があるということです。 Boostには1つあり、libsigcと他の多くがあります。
これの何が問題なのですか?
(Base(*this).*fp)();
今、それで満足したら、そもそもなぜ関数ポインターを使用しているのかという疑問が生じます。もう少しコンテキストが役立つと思います。