我对vtables的理解是,如果我有一个类,其类具有带有子类Lion和HouseCat的虚函数speak(),则存在一个vtable,该表将speak()映射到每个子类的正确实现。所以打个电话
cat.speak()
编译为
cat.vtable[0]()
也就是说,在vtable位置0进行查找,并在此位置调用函数指针。
我的问题是:多重继承会发生什么?
让我们添加一个Pet类。宠物具有虚拟函数speak()和eat()。 HouseCat扩展Pet,而Lion不扩展。现在,我需要确保
pet.eat()
编译为
pet.vtable[1]()
那就是vtable [0]需要是say()。 Pet.eat必须位于插槽1中。这是因为cat.speak()需要访问vtable中的插槽0,并且如果对于HouseCat来说,插槽0恰好被吃掉了,这将是非常错误的。
编译器如何确保vtable索引适合在一起?
最佳答案
规范没有设置任何东西,但是通常编译器会为每个直接的非虚拟基类生成一个vtable,为派生类生成一个vtable-然后第一个基类的vtable和派生类的vtable将是合并。
因此,更具体地说,编译器在构造类时生成的内容:
[vptr | Cat fields]
[0]: speak()
[vptr | Pet fields]
[0]: eat()
[vptr | Cat fields | Lion fields]
[0]: speak()
[vptr | Cat fields | vptr | Pet fields | HouseCat fields]
[0]: speak() [0]: eat()
编译器在调用/强制转换时生成的内容(变量名是静态类型名):
cat.speak()
obj[0][0]()
-适用于Cat,Lion和HouseCat的“猫”部分pet.eat()
obj[0][0]()
-适用于Pet和HouseCat lion.speak()
obj[0][0]()
-适用于Lion houseCat.speak()
obj[0][0]()
-对HouseCat houseCat.eat()
obj[Cat size][0]()
-对HouseCat (Cat)houseCat
obj
(Pet)houseCat
obj + Cat size
因此,我想让您感到困惑的关键是:(1)可能有多个vtable,而(2)upcast可能实际上返回了不同的地址。