如何在c ++和c#中实现vtables?

让我们有这种情况(在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#编译器是否始终创建一个vtable? 在投射时是否会进行任何指针修正?

不要过分迂腐,但C#编译器不参与此级别。 整个类型模型,inheritance,接口实现等实际上由CLR处理,更具体地说是CTS(通用类型系统)。 .NET编译器通常只生成表示意图的IL代码,后者由CLR执行,其中所有Vtable处理等都由此处理。

有关CLR如何创建和管理运行时类型的一些细节,以下链接将是一个很好的起点。 接下来解释了MethodTable和Interface Maps。

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

如果我用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]); } 

事实certificate,在C ++中b == a + 1,所以X的结构是[vtable-X + A] [vtable-B] [magic] [var]检查更深(nm ./a.out),vtable -X + a包含对X :: func的引用(正如人们所期望的那样)。 当你将X转换为B时,它会调整指针,以便将VTBL for B函数显示在代码所需的位置。

你真的打算“隐藏”B :: func()吗?

B的vtbl看起来像是对X的“trampoline”的引用,它在调用X + A vtbl所持有的“常规”X :: func之前将对象指针恢复为完整的X.

 080487ea <_zthn8_n1x4funcev>: # in "XB 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 XA 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 

是的,托管语言中只有一个v表,CLR不支持多重inheritance。 当您转换为已实现的接口时,会有一个指针修复。

当尝试声明一个COM接口时,这是一个值得注意的问题,该接口本身是从IUnknown之外的另一个接口声明的。 这篇文章的作者不太了解这个问题。 COM需要为每个接口提供单独的 v表,这正是支持MI的编译器所做的。

vtables是一个实现细节。 没有正式/必要/预期的实施。 不同的编译器供应商可以不同地实现inheritance