C++ 虚拟继承中派生类的大小
在上面的代码中,为什么ClassD的输出是16。请给我解释清楚这个虚拟继承是如何工作的。当ClassD继承ClassB和ClassC时,将有两个VPTR(一个来自B,一个来自C)。Scott Meyers的“更有效的C++”第24项(各种语言功能的成本)中描述了这种情况。当ClassD继承ClassB和ClassC时,将有两个VPTR(一个来自B,一个来自C)。Scott Meyers的“更有效的C++”第24项(各种语言功能的成本)中描述了这种情况。虚拟继承意味着虚拟基类只存在一次而不是多次。这就是为什么C++ 虚拟继承中派生类的大小,c++,sizeof,virtual-inheritance,memory-layout,vptr,C++,Sizeof,Virtual Inheritance,Memory Layout,Vptr,在上面的代码中,为什么ClassD的输出是16。请给我解释清楚这个虚拟继承是如何工作的。当ClassD继承ClassB和ClassC时,将有两个VPTR(一个来自B,一个来自C)。Scott Meyers的“更有效的C++”第24项(各种语言功能的成本)中描述了这种情况。当ClassD继承ClassB和ClassC时,将有两个VPTR(一个来自B,一个来自C)。Scott Meyers的“更有效的C++”第24项(各种语言功能的成本)中描述了这种情况。虚拟继承意味着虚拟基类只存在一次而不是多次。
ClassA
中的8个字节只在ClassD
中出现一次。虚拟继承本身需要一定的开销,因此您会得到一个额外的指针。精确的实现和确切的开销不是由C++标准指定的,并且可以根据您创建的层次而变化。 虚拟继承意味着虚拟基类只存在一次而不是多次。这就是为什么
ClassA
中的8个字节只在ClassD
中出现一次。虚拟继承本身需要一定的开销,因此您会得到一个额外的指针。确切的实现和确切的开销没有由C++标准指定,并且可以根据您创建的层次而变化。
虚拟基类与虚拟函数完全相同:它们的地址(或相对地址,又称偏移量)在编译时是未知的:
size of ClassA = 8
size of ClassB = 12
size of ClassC = 12
size of ClassD = 16
在这里,编译器必须计算ClassA
基子对象与ClassB
子对象(或大部分派生对象)的偏移量。有些编译器只是在ClassB
中有一个指向它的指针;其他人使用vtable,就像用于虚拟函数一样
在这两种情况下,ClassB
中的开销都是一个指针
ClassC
类似,但vptr将指向ClassC
vtable,而不是ClassB
vtable
因此,ClassD
对象将包含(这不是一个有序列表):
- 单个
子对象ClassA
- a
科目B类
- a
科目c类
ClassD
有两个继承的vptr:fromClassB
和ClassC
。在ClassD
对象中,两个vptr都将指向一些ClassD
vtable,但都是相同的ClassD
vtable:
- a
subject指向ClassD vtable中的ClassB,表示ClassB
base与ClassA
base的相对位置ClassB
主题指向ClassD vtable中的ClassC,表示ClassC
base与ClassA
base的相对位置ClassC
ClassB
和ClassC
将共享同一个vptr:给定d
一个ClassD
实例:
d.ClassB::vptr==&d.ClassC::vptr
sod.ClassB::vptr==d.ClassC::vptr
但是d.ClassB::vptr==&ClassC\u in_ClassD\u vtable
和d.ClassC::vptr==&ClassC\u in_ClassD\u vtable
,因此ClassD\u vtable中的ClassB\u必须与ClassC\u统一。在这种特殊情况下,ClassB\u In_ClassD\u vtable
和ClassC\u In_ClassD\u vtable
仅用于描述ClassA
子对象的偏移量;如果ClassB
和ClassC
子对象在ClassD
中统一,则这些偏移量也统一,因此可以统一vtables
请注意,这仅在这里是可能的,因为存在完全相似性。如果对ClassB
和ClassC
进行了修改,以便在每个虚拟函数中添加一个虚拟函数,例如这些虚拟函数不等价(因此不可统一),则无法实现vtable的统一
结论
这种优化只在像这样非常简单的情况下才可能实现。这些情况并不典型的C++编程:人们通常使用虚拟基类结合虚拟函数。空基类优化是有用的,因为许多C++习语使用没有数据成员或虚拟函数的基类。OTOH,一个用于虚拟基类特殊用途的微小(一个vptr)空间优化对于现实世界的程序似乎没有用处。虚拟基类实现
虚拟基类与虚拟函数完全相同:它们的地址(或相对地址,又称偏移量)在编译时是未知的:
size of ClassA = 8
size of ClassB = 12
size of ClassC = 12
size of ClassD = 16
在这里,编译器必须计算ClassA
基子对象与ClassB
子对象(或大部分派生对象)的偏移量。有些编译器只是在ClassB
中有一个指向它的指针;其他人使用vtable,就像用于虚拟函数一样
在这两种情况下,ClassB
中的开销都是一个指针
ClassC
类似,但vptr将指向ClassC
vtable,而不是ClassB
vtable
因此,ClassD
对象将包含(这不是一个有序列表):
- 单个
ClassA
子对象
- a
B类
科目
- a
c类
科目
因此ClassD
有两个继承的vptr:fromClassB
和ClassC
。在ClassD
对象中,两个vptr都将指向一些ClassD
vtable,但都是相同的ClassD
vtable:
void f(ClassB *pb) {
ClassA *pa = pb;
}