Data语义学——前言

假设现有多重继承体系如下:

1
2
3
4
class X {};
class Y:public virtual X {};
class Z:public virtual X {};
class A:public Y,public Z {};

上述X、Y、Z、A中没有任何一个class内含明显的数据,它们之间只存在继承关系,因此理论上每一个class的大小都应该为0,但事实上,它们的大小均不为0:
1
2
3
4
sizeof(X) == 1;
sizeof(Y) == 4;
sizeof(Z) == 4;
sizeof(A) == 8;

一个空的class如:

1
class X {};

事实上并非是空的,它有一个隐晦的1byte,那是被编译器安插进去的一个char(Effective C++ 40)。因此这个class具现化得到的object能够在内存中配置独一无二的地址:
1
2
X a,b;
if(&a == &b)...

那为什么Y和Z的大小会是4呢,这与三个因素有关:

  1. 语言本身的额外负担
    当语言支持virtual base classes时,就会导致一些额外负担。在derived class中,这个额外负担反应在某种形式的指针身上,它或者指向virtual base class subobject,或者指向一个相关表格:表格中存放的是virtual base class subobject的地址或其偏移量。
  2. 编译器对于特殊情况所做的优化处理
    Virtual base class X subobject的1 bytes大小也出现在class Y和Z上。传统上它被放在derived class的固定(不发生变动)部分的末端。有些编译器会对empty virtual base class提供特殊支持,但有的没有。
  3. Alignment的限制
    在大部分机器上,群聚的结构体大小会受到alignment的限制,使它们能够更有效的在内存中被存取。

每一个class object都必须有足够大小来容纳其所有nonstatic data members,有时候它们的大小可能会和我们预想的要大一些,原因在于:

  1. 由编译器自动加上的额外data members,用以支持某些语言特性(主要是virtual特性)。
  2. 因为alignment的需要。