Data 语意学
- {P83} 若干类的继承关系如下:
class X{};
class Y : public virtual X{};
class Z : public virtual X{};
class A: public Y, public Z{};
-
class X, 一个empty class,占用1 byte,是被编译器安插进去的一个char,目的是为了一个class的两个objects得以在内存中拥有独一无二的地址,以区分不同的对象。
-
{P85} Y和Z的大小受到三个因素的影响:
- 语言本身所造成的额外负担overhead。语言支持virtual base classes时导致的额外负担反映在某种形式的指针身上,它要么指向virtual base class subobject,要么指向一个存放virtual base class subobject地址或者其偏移量offset的表格。
- 编译器对于特殊情况所提供的优化处理。virtual base class X 1 byte大小的subobject也出现在class Y和Z身上。
- Alignment的限制。Alignment就是将数值调整到某数的整数倍。
-
一个virtual base class subobject只会在derived class中存在一份实体,不管它在class继承体系中出现了多少次。
C++ standard并不强制规定base class subobjects、不同存取级别的data members的排列次序这种琐碎细节,它也不规定virtual function以及virtual base classes的实现细节。
static vs. non-static {P87-88}
- C++对象模型尽量以空间优化和存取速度优化来表现nonstatic data members,并且保持和C语言struct数据配置的兼容性。它把数据直接存放在每一个class object中,对于继承而来的nonstatic data members,不管是virtual或nonvirtual base class也是如此。
- 至于static data members则被放置在程序的一个global data segment中,不会影响个别class object的大小。static data member永远只存在一份实体,但是一个template class的static data member的行为稍有不同。
Data Member的绑定 {P89-91}
- 对于class member function本体的分析,会在整个class声明完成后才发生。所以我们不用担心引用的x是class成员x或者是外部的x,古老的C++需要担心这个问题,于是成员声明都写在最开头。
- 而对于成员函数argument list中的名称还是会在它们第一次遭遇时被适当地决议resolved完成。基于这种状况,请始终把nested type声明放在class的起始处。
Data Member的布局
-
{P92} C++ Standard要求,在同一个access section中,members的排列只需满足“较晚出现的members在class object中有较高的地址”这一条件即可。也就是说各个members并不一定的连续排列,alignment可能需要的bytes以及编译器可能合成供内部使用的data members都可能介于被声明的members之间。
-
{P93} C++ Standard也允许编译器将多个access sections之中的data members自由排列,不必在乎它们出现在class声明中的次序。
-
{P94} 判断哪一个section先出现的template function:
template <class class_type, class data_type1, class data_type2>
char* access_order(data_type1 class_type::*mem1,
data_type2 class_type::*mem2)
{
assert(mem1 != mem2);
return mem1 < mem2 ?
“member 1 occurs first” : “member 2 occurs first”;
}
access_order(&Point3D::z, &Point3D::y);
Data Member的存取
- {P95} static data member只有一个实体,程序的data segment之中
- {P97} 有多少个编译器,就有多少种name-mangling做法
really???
任何name-mangling做法都有两个要点:- 一种算法,推导出独一无二的名称;
- 如果编译系统或者环境工具必须和使用者交互,那些独一无二的名称可被轻易推导回原先的名称。
- {P97} non-static data members直接放在每一个class object之中,除非经过显示的explicit或隐含的implicit class object,否则没有办法直接存取它们。
implicit主要发生在成员函数中,其实质是编译器会增添一个const this指针,而在函数体内通过这个this指针来存取non-static data member
。
继承与Data Member
-
{P99} 在C++继承模型中,一个derived class object所表现出来的东西,是其自己的members加上其base classes members的总和。C++并未规定derived class members和base classes members的排列次序。不过,在大部分编译器上,除virtual base class外,base class members总是先出现。
-
一般而言,具体继承(concrete inheritance 相对于虚拟继承) 并不会增加空间或存取时间上的额外负担。
-
容易犯的错误: {P102} 把一个class分解为多层,有可能会为了表现class体系之抽象化而膨胀所需空间(因为编译器的边界调整)。其根本原因是C++保证出现在derived class中的base class subobject有其完整原样性。所以拆开后每个如果需要对齐,将会额外多出空间。如果不这么支持,从派生类到基类的转换将会出问题。
多态
- {P111} 把vptr放在class object的尾端,可以保留base class C struct的对象布局,因而允许在C程序中也能使用。在C++问世之初被大量采用。
- 把vptr放在class object的开始处,对于“在多重继承下,通过指向class members的指针调用virtual function,会带来一些帮助。否则,不仅“从class object 起始点开始量起”的offset必须在执行期备妥,甚至与class vptr之间的offset也必须备妥。当然,放在前端,代价就是丧失了C语言兼容性。
多重继承
- 多重继承的复杂度在于derived class和其上一个base class乃至上上一个base class之间的“非自然关系” {P112},其主要问题发生在derived class objects和其第二或后继的base class objects之间的转换。{P113-115} 举个例子:
class A {int x;};
class A1 : public A {int y;};
class B {int z;}
class AB : public A1, B {int a;}
AB ab;
A* pa = &ab; 直接ab
A1* pa1 = &ab; 直接ab
B* pb = &ab; 非自然转换 &ab?(B*)((char*)&ab+sizeof(A1)):0;
- C++并未要求多重继承时derived class object中各个base class subjectes的排列次序,目前各个编译器都是根据声明次序来排列它们。
- 这里对成员的访问无需额外成本,因为offset是固定的!
虚拟继承
{P117-P118} Class内如果内含一个或多个virtual bass class subobjects,像iostream那样,将被分割为两部分:一个不变区域和一个共享区域。不变区域的数据,不管后继怎么变化,总是拥有固定的offset,可以直接存取;而共享区域表现的就是virtual base class subobject,这一部分的数据,其位置会因为每次的派生操作而变化,只可间接存取。各家编译器实现技术之间的差异就在于间接存取的方法不同。
一般而言,virtual base class最有效的一种运用方式是:一个没有任何data member的抽象class。
指向Data Members的指针
{P129} 一个有点神秘但颇有用处的语言特性,特别是如果你需要详细调查class members的底层布局。这样的调查可用来决定vptr是放在class的起始处还是尾端。另一个用途,还可用来决定class中access sections的次序。
printf("%p\n", &Point3d::x); // 4
printf("%p\n", &Point3d::y); // 8
printf("%p\n", &Point3d::z); // C ⇒ 这里存的是offset
如果x,y,z是连续的,而且是从0开始的,说明vptr在尾端。否则,vptr在前面。