子类中如何生成虚函数table

How is virtual function table generated in subclass

所以我知道在 C++ 中虚拟方法是针对存储在 table 中的每个 class 并且每个实例都有一个指向那个 table 的指针。所以我的问题是 subclass table 长什么样。我将提供一个汇编指令:

vtable for Derived:
    .long   0
    .long   _typeinfo for Derived
    .long   _Derived::set()
    .globl  _vtable for Base
    .section    .rdata$vtable for Base,"dr"
    .linkonce same_size
    .align 4

所以,从这里我可以看到 Derived 有一个虚拟方法,它是 set(),但让我感到困扰的部分是 Base 的 vtable。 Derived vptr 是否持有指向 Base vptr 的指针,或者它是否存储在 Derived 的 vtable 中。 我注意到在代码编译器中只将 vtable 存储在对象的 0 地址,一次在 Base 构造函数中,一次在 Derived 中。为什么 vtable 没有被覆盖?

P.S。我不太懂指令

编辑:

class Base{
   virtual void print() {
       printf("Base");
   }
}

class Derived : Base{
   virtual void print(){
       printf("Derived");
   }
}

简单的回答是:这取决于编译器。该标准未指定应如何实施,仅指定其行为方式。

在实践中,实现倾向于简单地有一个派生的 vtable,它以与基础 vtable 相同的结构开始,而不是在末尾附加派生的 class 方法(也就是说派生 class 中的新方法,而不是覆盖)。

vtable指针正好指向整个table的开始。如果通过基本指针类型访问对象,那么任何人都不应该查看基本 class 方法的末尾。

如果指针是派生类型,则同一指针将允许进一​​步向下访问 table 到派生 class 中声明的虚拟方法。

附录:多重继承

多重继承遵循相同的基本概念,但由于显而易见的原因很快变得复杂。但有一项重要功能需要牢记。

一个多重派生的class有一个vtable指针指向其基数class的每个,指向不同的vtables 或同一 vtable 中的不同位置(取决于实现)。

但重要的是要记住它是每个直接基础 class 每个对象.

因此,如果您有一个包含一个 int 数据和三个直接基数 class 的乘法派生 class,则每个对象的大小实际上为 16 个字节(在32 位系统;更多关于 64 位)。 int 4 个,每个 vtable 指针各 4 个。另外,当然,每个基数的大小 classes 本身。

这意味着在 C++ 中,接口并不便宜。 (显然在 C++ 中没有真正的接口,但是一个没有数据的基础 class 并且只有纯虚拟方法模拟它。)每个这样的接口花费一个指针的大小 每个对象 .

在 C# 和 Java 等语言中,接口是语言的一部分,机制略有不同,所有接口都通过单个 vtable 指针进行路由。这稍微慢一些,但意味着每个对象只有一个 vtable 指针,但是实现了很多接口。

出于设计原因,我仍然会遵循 C++ 中的接口样式方法,但始终要注意这种额外的开销。

(其中 none 甚至涉及虚拟继承。)