我对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()
    
  • HouseCat
    [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可能实际上返回了不同的地址。

    08-16 09:31