我对vtable的理解是,如果我有一个带有虚函数的Cat,带有子类Lion和HouseCat的说话,那么有一个vtable,它将speak()映射到每个子类的正确实现。所以打电话
cat.speak()
编译到
cat.vtable[0]()
即,在vtable位置0中查找并在此位置调用函数指针。
我的问题是:多重继承会发生什么?
让我们添加一个Pet类。 Pet有虚函数speak()和eat()。 HouseCat扩展了Pet,而Lion则没有。现在,我需要确保
pet.eat()
编译为
pet.vtable[1]()
那是vtable [0]需要说话()。 Pet.eat需要是插槽1.这是因为cat.speak()需要访问vtable中的插槽0,如果对于HouseCat,插槽0恰好吃了,这将是非常错误的。
编译器如何确保vtable索引适合在一起?
答案 0 :(得分:1)
规范没有设置任何内容,但通常编译器会为每个直接的非虚拟基类生成一个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的“Cat”部分pet.eat()
obj[0][0]()
- 适用于宠物和HouseCat的“宠物”部分lion.speak()
obj[0][0]()
- 对Lion有效houseCat.speak()
obj[0][0]()
- 适用于HouseCat的“Cat”部分houseCat.eat()
obj[Cat size][0]()
- 适用于HouseCat的“宠物”部分(Cat)houseCat
obj
(Pet)houseCat
obj + Cat size
所以我想让你困惑的关键是(1)多个vtable是可能的,(2)upcasts实际上可能会返回不同的地址。