Ant*_*cic 0 c++ polymorphism assembly vtable
所以我知道在 C++ 中,虚拟方法适用于存储在表中的每个类,并且每个实例都有一个指向该表的指针。所以我的问题是子类表是什么样的。我将提供一个汇编指令:
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
Run Code Online (Sandbox Code Playgroud)
因此,从这里我可以看到 Derived 有一个虚拟方法set(),但令我烦恼的部分是 Base 的 vtable。Derived vptr 是否保存指向 Base vptr 的指针,还是存储在 Derived 的 vtable 内。我注意到,在代码中,编译器仅将 vtable 存储在对象的 0 地址处,一次存储在 Base 构造函数中,一次存储在 Derived 中。为什么vtable没有被覆盖?
PS我不太明白指令
编辑:
class Base{
virtual void print() {
printf("Base");
}
}
class Derived : Base{
virtual void print(){
printf("Derived");
}
}
Run Code Online (Sandbox Code Playgroud)
简单的答案是:这取决于编译器。该标准没有指定应如何实现,仅指定其行为方式。
实际上,实现往往只是有一个派生 vtable,它以与基 vtable 相同的结构开始,然后在末尾附加派生类方法(也就是说派生类中的新方法,而不是重写)。
vtable指针只是指向整个表的开头。如果通过基指针类型访问对象,那么任何人都不应查看基类方法末尾之外的内容。
如果指针是派生类型,则同一指针将允许访问表中派生类中声明的虚拟方法。
附录:多重继承
多重继承遵循相同的基本概念,但由于显而易见的原因很快就会变得复杂。但有一个重要特征需要牢记。
多重派生类的每个基类都有一个 vtable 指针,指向不同的 vtable 或同一 vtable 中的不同位置(取决于实现)。
但重要的是要记住它是每个对象的每个直接基类一个。
因此,如果您有一个包含int数据之一和三个直接基类的多重派生类,则每个对象的大小实际上为 16 字节(在 32 位系统上;在 64 位系统上更多)。每个 vtable 指针有 4 个,int每个 vtable 指针有 4 个。当然,还有每个基类本身的大小。
这意味着在 C++ 中,接口并不便宜。(显然,C++ 中没有真正的接口,但没有数据且只有纯虚方法的基类可以模拟它。)每个此类接口的成本相当于每个对象的指针大小。
在 C# 和 Java 等语言中,接口是语言的一部分,有一种略有不同的机制,所有接口都通过单个 vtable 指针进行路由。这稍微慢一些,但意味着每个对象只有一个 vtable 指针,无论实现了多少接口。
出于设计原因,我仍然会遵循 C++ 中的接口样式方法,但始终要注意这种额外的开销。
(这些甚至都没有涉及虚拟继承。)