仮想メンバー関数へのポインター。どのように機能するのでしょうか?

StackOverflow https://stackoverflow.com/questions/1087600

質問

次の C++ コードを考えてみましょう。

class A
{
public:
      virtual void f()=0;
};


int main()
{
     void (A::*f)()=&A::f;
}

推測する必要があるとすれば、この文脈での &A::f は、通常のメンバー関数へのポインターと仮想メンバー関数へのポインターの間に明示的な分離がないため、「A の f() 実装のアドレス」を意味すると思います。 。A は f() を実装していないため、コンパイル エラーになります。しかし、そうではありません。

それだけではありません。次のコード:

void (A::*f)()=&A::f;
A *a=new B;            // B is a subclass of A, which implements f()
(a->*f)();

実際には B::f を呼び出します。

それはどのようにして起こるのでしょうか?

役に立ちましたか?

解決

ここでは、メンバー関数ポインターに関する情報が多すぎます。「The Well-Behaved Compilers」には仮想関数に関するものがいくつかありますが、記事は実際には C++ でのデリゲートの実装に関するものであったため、IIRC が記事を読んだときはその部分を流し読みしていました。

http://www.codeproject.com/KB/cpp/FastDelegate.aspx

端的な答えは、コンパイラに依存するということですが、可能性の 1 つは、メンバー関数ポインターが、仮想呼び出しを行う「サンク」関数へのポインターを含む構造体として実装されていることです。

他のヒント

標準はそれはそれが起こるべきかだと言うので、それは動作します。 GCC店は仮想テーブルはバイト単位で、当該の機能のオフセット、私はGCCとのいくつかのテストをしました、そして、それは仮想関数のために判明します。

struct A { virtual void f() { } virtual void g() { } }; 
int main() { 
  union insp { 
    void (A::*pf)();
    ptrdiff_t pd[2]; 
  }; 
  insp p[] = { { &A::f }, { &A::g } }; 
  std::cout << p[0].pd[0] << " "
            << p[1].pd[0] << std::endl;
}
これら2つの関数の仮想テーブルエントリのバイトオフセット -

そのプログラムは1 5を出力します。これは、のItaniumのC ++ ABI の、追従しますそのに指定します。

私は完全に一定ではないんだけど、私はそれだけで通常の多型の行動だと思います。私は&A::fが実際にクラスのvtableの中に関数ポインタのアドレスを意味していることを考えると、あなたはコンパイラエラーを取得されていません理由です。 vtableの中にスペースがまだ割り当てられ、それはあなたが実際に戻ってきている場所です。

派生クラスは、基本的にその関数へのポインタで、これらの値を上書きするため、

これは理にかなっています。これはあなたの第二の例では、なぜ(a->*f)()作品である - fは、派生クラスで実装されているのvtableを参照している

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top