如果我理解正确,类定义会在vtable中强加虚函数的某个顺序,因此已知给定函数与表的开头有一定的偏移量。但是,我不明白它是如何与多态性一起工作的。
class B1 {
virtual void funcB1();
};
class B2 {
virtual void funcB2() {}
};
class D : public B1, public B2 {
virtual void funcB1() {}
virtual void funcB2() {}
};
void main(...) {
B1 *b1 = new D();
B2 *b2 = new D();
B1 *realB1 = new B1();
B2 *realB2 = new B2();
b1->funcB1();
b2->funcB2();
realB1->funcB1();
realB2->funcB2();
}
生成的代码如何知道如何在不同的偏移量下访问funcB2?
答案 0 :(得分:1)
通常D
对象将有两个 vtable指针,每个基类一个。它确实无法避免,因为它必须包含每个基类的相同二进制布局。无论何时从一种类型转换为另一种类型,编译器都会插入指针修正 - 如果在转换为每个基类后打印指针地址,您将看到它们是不同的。
答案 1 :(得分:1)
当您从两个基类组成一个类时,每个部分都由一个功能完备的块在结果类中表示,并带有自己的指向vtable的指针。这就是生成的代码如何知道要调用的函数:将D
的指针强制转换为B1
和B2
会产生不同的指针,因此生成的代码可以使用相同的偏移量进入虚拟表
D *d = new D();
B1 *b1 = dynamic_cast<B1*>(d);
B2 *b2 = dynamic_cast<B2*>(d);
printf("%p %p %p", (void*)d, (void*)b1, (void*)b2);
这produces the following output on ideone:
0x91c7008 0x91c7008 0x91c700c
请注意D*
和B1*
如何打印相同的值,而B2*
打印不同的值。当您致电b2->funcB2()
时,指针b2
已经指向D
对象的不同部分,该部分指向不同的vtable(布局为B2
的vtable ),因此生成的代码不需要对您的示例中的b2
vs realB2
执行任何不同的操作。