第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*
)。