第3章:Data语意学
The size of class
1 | class X |
一个类的大小主要受三个因素的影响:
语言支持的特性所造成的负担
比如,含虚函数的类会额外多一个
vptr指针,含virtual base class会再多一个额外的指针(它或者指向virtual base class subobject,或者指向一个相关表格;表格中存放的若不是virtual base class subobject地址,就是其偏移量)。编译器对于特殊情况所提供的优化处理
现代编译器的对于空基类的优化处理:一个
empty virtual base class被视为derived class object最开头的一部分,也就是说它并没有花费任何的额外空间。因为既然有了members,就不需要原本为了empty class而安插的一个char。alignment(内存对齐)就是将数值调整到某数的整数倍。在
64位计算机上,通常alignment为8 bytes,以使bus的“运输量”达到最高效率。
nonstatic data members放置的是“个别的class object”感兴趣的数据,static data members则放置的是“整个class”感兴趣的数据。
static data members被放置在程序的一个global data segment中,不会影响个别的class object的大小。在程序之中,不管该class被产生出多少个objects(经由直接产生或间接派生),static data member永远只存在一份实体(即使该class没有任何object实体,其static data members也已存在)。
3.1 data member的绑定
类成员函数的argument list中的名称会在它们第一次遭遇时被适当地决议(resolved)完成。因此在extern和nested type name之间的非直觉绑定操作还是会发生。例如在下面的程序片段:
1 | typedef std::string length; |
所以这需要使用防御性的程序风格:将nested type name写在class的起始处!
3.2 数据成员的布局
下面这个template function,接受两个data member,然后判断谁先出现在class object之中。如果两个member都是不同的access sections中的第一个被声明者,此函数就可以用来判断哪一个section先出现:
1 | template<class class_type, class data_typel, class data_type2> |
上述函数可以这样被调用:
1 | access_order(&Point3d::y, &Point3d::z); |
3.3 data member的存取
static data member
对于类中的静态数据成员,通过一个指针和通过一个对象来存取,效率完全相同,不论这个静态数据成员经过了多么复杂的继承体系。
若取一个static data member的地址不会得到指向其class member的指针(不是value_type class_type::*类型)。而是指向其数据类型的指针(类型为value_type*)。
1 | class Point |
构造函数不能是静态成员函数:如果构造函数是静态成员函数,那么将不能访问非静态变量,也没办法完成初始化的工作。
non static data member
对于类中的非静态数据成员,通过一个指针和通过一个对象来存取,当此数据成员属于继承而来的virtual base class时,使用指针效率较低。如果使用对象直接存取,就不会有这些问题,其类型无疑是确定的,而即使它继承自virtual base class,members的offset位置也在编译时期就固定了。
欲对一个nonstatic data member进行存取操作,编译器需要把class object的起始地址加上data member的偏移量。举个例子:
1 | &origin._y == &origin + (&Point3d::_y - 1); |
请注意其中的-1操作。指向data member的指针,其offset值总是被加上1,这样可以使编译系统区分出是用以指出class的第一个member还是没有指出任何member的两种情况。
3.4 C++对象布局
个别
struct的数据布局
单一继承而且没有
virtual function时的数据布局
C++语言保证——出现在derived class中的base class subobject有其完整原样性

单一继承并含虚拟函数情况下的数据布局

多重继承


虚拟继承,使用
pointer strategy和virtual table offset strategy


3.5 指向data member成员的指针
取一个nonstatic data member的地址,将会得到它在class中的offset(指针类型为data_type class_type::*),取一个“绑定于真正class object身上的data member”的地址,将会得到该member在内存中的真正地址(指针类型为data_type*)。