虚拟继承中派生类的大小
Posted
技术标签:
【中文标题】虚拟继承中派生类的大小【英文标题】:size of derived class in virtual inheritance 【发布时间】:2013-02-26 10:09:42 【问题描述】:#include "stdafx.h"
#include <iostream>
using namespace std;
class ClassA
protected:
int width, height;
public:
void set_values(int x, int y)
width = x;
height = y;
;
class ClassB : virtual public ClassA
//12(int + int + vptr)
;
class ClassC : virtual public ClassA
//12(int + int + vptr)
;
class ClassD : public ClassB, public ClassC
;
int main()
ClassA A;
ClassB B;
ClassC C;
ClassD D;
cout << "size = " << sizeof(A) << endl;
cout << "size = " << sizeof(B) << endl;
cout << "size = " << sizeof(C) << endl;
cout << "size = " << sizeof(D) << endl;
return 0;
我得到的输出是:
size of ClassA = 8
size of ClassB = 12
size of ClassC = 12
size of ClassD = 16
在上面的代码中,为什么 ClassD 的输出是 16。请清楚地解释一下这个虚拟继承是如何工作的。
【问题讨论】:
这取决于实现。无论如何,您为什么要依靠大小来确定特定的东西?如果您需要尺寸,只需使用sizeof
,它会返回适当的尺寸。出于理论目的知道这一点很好,但这绝对没有实际应用。
int + int + 2 * vptr?
只是为了了解这个虚拟继承。根据我的说法,ClassD 的大小应该是 24 字节,即 (int + int + vptr) + (int + int + vptr) = 24 bytes 。所以我很困惑这是如何工作的所以问..
@Jon:虽然这里没有虚函数,所以应该不需要任何 vptrs,AFAICS。
@OliCharlesworth 需要 vtable 才能定位 ClassA
子对象。
【参考方案1】:
虚拟继承意味着虚拟基类只存在一次,而不是多次。这就是为什么来自ClassA
的8 个字节只在ClassD
中出现一次。虚拟继承本身需要一定的开销,因此您会得到一个额外的指针。 C++ 标准没有指定确切的实现以及因此的确切开销,并且可能会根据您创建的层次结构而有所不同。
【讨论】:
"12 个字节来自" 你的意思是 8 个字节吗?【参考方案2】:当 ClassD 继承 ClassB 和 ClassC 时,将有两个 vptr(一个来自 B,一个来自 C)。 Scott Meyers 的“更有效的 C++”第 24 条(各种语言特性的成本)中描述了这种确切的情况。
【讨论】:
【参考方案3】:虚拟基类实现
虚拟基类与虚拟函数完全一样:它们的地址(或相对地址,也就是偏移量)在编译时是未知的:
void f(ClassB *pb)
ClassA *pa = pb;
这里编译器必须计算ClassA
基本子对象与ClassB
子对象(或主要是派生对象)的偏移量。一些编译器只是在ClassB
中有一个指向它的指针;其他人使用 vtable,就像虚拟功能一样。
在这两种情况下,ClassB
的开销都是一个指针。
ClassC
类似,但 vptr 将指向 ClassC
vtable,而不是 ClassB
vtable。
因此,ClassD
对象将包含(这不是有序列表):
ClassA
子对象
ClassB
主题
ClassC
主题
所以ClassD
有两个继承的vptr:来自ClassB
和ClassC
。在ClassD
对象中,两个vptr 都将指向一些 ClassD
vtable,但相同的ClassD
vtable:
ClassB
主题指向 ClassB-in-ClassD vtable,表示 ClassA
base 与 ClassB
base 的相对位置
一个ClassC
主题指向ClassC-in-ClassD vtable,表示ClassA
base与ClassC
base的相对位置
可能的优化
我猜你的问题是:我们需要两个不同的 vptr 吗?
从技术上讲,有时可以通过覆盖基类子对象来优化类的大小。这是技术上可行的情况之一:
重叠(或统一)意味着ClassB
和ClassC
将共享相同的vptr:给定d
一个ClassD
的实例:
&d.ClassB::vptr == &d.ClassC::vptr
所以d.ClassB::vptr == d.ClassC::vptr
但d.ClassB::vptr == &ClassC_in_ClassD_vtable
和d.ClassC::vptr == &ClassC_in_ClassD_vtable
,所以ClassB_in_ClassD_vtable
必须与ClassC_in_ClassD_vtable
统一。在这种特殊情况下,ClassB_in_ClassD_vtable
和ClassC_in_ClassD_vtable
都只用于描述ClassA
子对象的偏移量;如果ClassB
和ClassC
子对象在ClassD
中统一,那么这些偏移量也是统一的,因此vtable 的统一是可能的。
请注意,这仅在此处是可能的,因为它们完全相似。如果 ClassB
和 ClassC
被修改为在每个中添加一个虚函数,例如这些虚函数不等价(因此不可统一),则 vtable 统一是不可能的。
结论
这种优化只有在像这样的非常简单的情况下才有可能。这些情况不是 C++ 编程的典型情况:人们通常将虚拟基类与虚拟函数结合使用。空基类优化很有用,因为许多 C++ 习惯用法使用没有数据成员或虚函数的基类。 OTOH,针对虚拟基类的特殊用途的微小(一个 vptr)空间优化似乎对现实世界的程序没有用。
【讨论】:
以上是关于虚拟继承中派生类的大小的主要内容,如果未能解决你的问题,请参考以下文章