假设有代码

class X{ };
class Y : public virtual X { };
class Z : public virtual X { };
class A : public Y, public Z { };
sizeof(X()); //1
sizeof(Y()); //8或4
sizeof(Z()); //8或4
sizeof(A()); //12或8

X、Y、Z、A内部均没有数据,但它们的大小却不是0。
一个空类的大小通常为1,是因为编译器为其安插了一个char,以便这个类的任意两个object能够在内存中配置独一无二的地址。 如X大小为1。
一个类的大小通常与机器和编译器有关,受以下三个因素的影响:

  1. 语言本身的额外负担 ,比如支持虚函数,或者虚继承。
  2. 编译器对于特殊情况的优化处理 ,例如某些编译器会对empty virtual base class提供特殊的支持,使其视为derived class object最开头的一部分,由于此时Y和Z并不是空类,所以并不需要多安插一个char,使Y()和Z()在某些编译器下为4而不是1+4+3.
  3. Alignment的限制(对齐原则) :内存对齐使得它们能够更有效地在内存中存取,Alignment指将数值调整至某数的整数倍,一般32位机器Alignment为4bytes.

而对于class A,它的大小由下面4点决定:

  1. Y和Z共享的class X的实例,1byte。
  2. Y和Z分别减去“因virtual base class X而配置”的大小,(4+4)byte
  3. class A本身大小,0byte
  4. alignment :1+4+4=9,需要补3bytes

但对于使用了empty virtual base class优化的编译器,A并不需要class X实例中的1个char,故alignment也不需要补,sizeof(A())=8。

1. Data Member的绑定

一个class定义中,Member function的argument list中的名称在它们第一次遭遇时便会被决议(resolved),即决定好其参数类型,如以下代码:

#include <iostream>
using namespace std;
typedef float length;//global type
class Point3d {
public:
    //参数列表中length被决议为global,即float
    void mumble(length val) { _val = val; }
    //_val被决议为Point3d::_val,即int
    length mumble() {
        cout << typeid(_val).name() << endl;
        return _val;
    }
private:
    typedef int length; //nested type
    length _val;
};
int main()
{
    Point3d p;
    p.mumble(); //i(int)
    cout << typeid(p.mumble()).name();  //f(float)
    return 0;
}

所以,<mark>总是把”nested type声明”放在class的起始处</mark>,确保绑定的正确性。