让我们有这种情况(在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?在投射时是否会进行任何指针修正?
答案 0 :(得分: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时,它会调整指针,以便将VTBL for B函数出现在代码所需的位置。
你真的打算“隐藏”B :: func()吗?
B的vtbl看起来像是对X的“trampoline”的引用,它在调用X + A vtbl所持有的“常规”X :: func之前将对象指针恢复为完整的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
答案 1 :(得分:2)
不要过分迂腐,但C#编译器不参与此级别。整个类型模型,继承,接口实现等实际上由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
答案 2 :(得分:2)
是的,托管语言中只有一个v表,CLR不支持多重继承。当您转换为已实现的接口时,会有一个指针修复。
当尝试声明一个本身是从IUnknown之外的另一个接口声明的COM接口时,这是一个值得注意的问题。 this article's作者不太了解的问题。 COM需要为每个接口提供一个单独的 v-table,就像支持MI的编译器一样。
答案 3 :(得分:1)
vtables是一个实现细节。没有正式/必要/预期的实施。不同的编译器供应商可以以不同的方式实现继承。