6

この状況を考えてみましょう(c ++では、c#クラスではA、Bはインターフェイスです):

class A { virtual void func() = 0; };
class B { virtual void func() = 0; };
class X: public A, public B { virtual void func(){ var = 1; } int var;};

X * x = new X; // from what I know, x have 2 vtables, is this the same in c#?
A * a = (A*)x; // a == x
B * b = (B*)x; // here b != x, so when calling b->func(), how is the address of var correct?

C#コンパイラは常に1つのvtableを作成しますか?キャスト時にポインタを修正しますか?

4

4 に答える 4

3

この派生バージョンをg++で調べた場合

class X: public A, public B { 
   unsigned magic;
 public:
   X() : magic(0xcafebabe) {};
   virtual void func(){ var = 1; } int var;
};

extern "C" int main() 
{
   X * x = new X; // from what I know, x have 2 vtables, is this the same in c#?
   A * a = (A*)x; // &a == &x
   B * b = (B*)x; // here &b != &x, so when calling b->func(), how is the address of var correct?
   printf("%p -- %p -- %p\n", x, a, b);

   unsigned* p = (unsigned*)((void*) x);
   unsigned *q = (unsigned*)(p[1]);
   printf("x=[%x %x %x %x]\n",p[0],p[1],p[2],p[3]);
   p = (unsigned*)(p[0]);
   printf("a=[%x %x %x %x]\n",p[0],p[1],p[2],p[3]);
   printf("b=[%x %x %x %x]\n",q[0],q[1],q[2],q[3]);

}

C++ではb== a + 1であるため、Xの構造は[vtable-X + A] [vtable-B] [magic] [var]であり、より深く(nm ./a.out)、vtableを検査します。 -X + aには、X :: funcへの参照が含まれています(予想どおり)。XをBにキャストすると、コードが予期する場所にB関数のVTBLが表示されるようにポインターが調整されました。

実際にB::func()を「隠す」つもりでしたか?

Bのvtblは、X +Avtblが保持する「通常の」X::funcを呼び出す前に、オブジェクトポインタを完全なXに復元するXへの「トランポリン」への参照を保持しているように見えます。

080487ea <_ZThn8_N1X4funcEv>:   # in "X-B vtbl"
_ZThn8_N1X4funcEv():
 80487ea:       83 44 24 04 f8          addl   $0xfffffff8,0x4(%esp)
 80487ef:       eb 01                   jmp    80487f2 <_ZN1X4funcEv>
 80487f1:       90                      nop

080487f2 <_ZN1X4funcEv>:        # in X-A vtbl
_ZN1X4funcEv():
 80487f2:       55                      push   %ebp
 80487f3:       89 e5                   mov    %esp,%ebp
 80487f5:       8b 45 08                mov    0x8(%ebp),%eax
 80487f8:       c7 40 14 01 00 00 00    movl   $0x1,0x14(%eax)
 80487ff:       5d                      pop    %ebp
 8048800:       c3                      ret    
于 2010-09-03T10:28:19.107 に答える
2

過度に衒学的ではありませんが、C#コンパイラはこのレベルには関与しません。全体の型モデル、継承、インターフェイスの実装などは、実際にはCLR、より具体的にはCTS(共通型システム)によって処理されます。.NETコンパイラは、ほとんどの場合、インテントを表すILコードを生成するだけです。このコードは、後ですべてのVtable処理などが処理されるCLRによって実行されます。

CLRがランタイムタイプを作成および管理する方法の詳細については、次のリンクが出発点として適しています。最後に、MethodTableとインターフェイスマップについて説明します。

http://web.archive.org/web/20150515023057/https://msdn.microsoft.com/en-us/magazine/cc163791.aspx

于 2010-09-03T10:06:06.697 に答える
2

はい、マネージド言語にはvテーブルが1つしかなく、CLRは多重継承をサポートしていません。実装されたインターフェースにキャストすると、ポインターの修正があります。

これは、IUnknown以外の別のインターフェイスからそれ自体が宣言されているCOMインターフェイスを宣言しようとする場合の注目すべき問題です。この記事の著者が完全には理解していない問題。COMは、MIをサポートするコンパイラが行うのと同じように、インターフェイスごとに個別のvテーブルを必要とします。

于 2010-09-03T10:08:55.883 に答える
1

vtablesは実装の詳細です。公式/必須/予想される実装はありません。コンパイラベンダーが異なれば、継承の実装も異なります。

于 2010-09-03T11:09:04.703 に答える