假设有代码
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。
一个类的大小通常与机器和编译器有关,受以下三个因素的影响:
- 语言本身的额外负担 ,比如支持虚函数,或者虚继承。
- 编译器对于特殊情况的优化处理 ,例如某些编译器会对empty virtual base class提供特殊的支持,使其视为derived class object最开头的一部分,由于此时Y和Z并不是空类,所以并不需要多安插一个char,使Y()和Z()在某些编译器下为4而不是1+4+3.
- Alignment的限制(对齐原则) :内存对齐使得它们能够更有效地在内存中存取,Alignment指将数值调整至某数的整数倍,一般32位机器Alignment为4bytes.
而对于class A,它的大小由下面4点决定:
- Y和Z共享的class X的实例,1byte。
- Y和Z分别减去“因virtual base class X而配置”的大小,(4+4)byte
- class A本身大小,0byte
- 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>,确保绑定的正确性。