28

次の 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 を呼び出します。

それはどのように起こりますか?

4

3 に答える 3

22

標準がそれが起こるべきであると言っているので、それは機能します。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;
}

そのプログラム1 5は、これら2つの関数の仮想テーブルエントリのバイトオフセットを出力します。Itanium C ++ ABIに従い、それを指定します。

于 2009-07-06T15:38:27.797 に答える
10

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

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

簡単に言うと、コンパイラーに依存しますが、メンバー関数ポインターが、仮想呼び出しを行う「サンク」関数へのポインターを含む構造体として実装されている可能性があります。

于 2009-07-06T15:36:20.513 に答える
1

完全にはわかりませんが、それは単なる通常のポリモーフィックな動作だと思います。&A::fこれは、実際にはクラスのvtable内の関数ポインターのアドレスを意味していると思います。そのため、コンパイラエラーは発生しません。vtable内のスペースはまだ割り当てられており、それが実際に戻ってくる場所です。

派生クラスは基本的にこれらの値をそれらの関数へのポインターで上書きするため、これは理にかなっています。これが(a->*f)()、2番目の例で機能する理由です-f派生クラスに実装されているvtableを参照しています。

于 2009-07-06T15:40:11.163 に答える