说明:
<u>不是很清楚的点</u>,用下划线。
解答,用斜体;
重点,用粗体加粗;
第二章 Data 语意学
3.2 Data Member 的布局
非静态成员变量在class object中的排列顺序将和其声明的顺序一样的。但C++ standard允许编译器将多个access sections之中的data members自由排列,不必在乎他们的出现在class中的声明顺序。
3.3 Data Member 的存取
存取代价:
每一个member 的存取许可(private public protected),以及与class的关联,并不会导致任何空间上或执行时间上的额外负担——不论是在个别的class objects 或是在static data member 本身。
static data members:
静态数据成员(static data members) 被视为global变量,只有一个实体,存放在程序的data segment(数据段)之中,每次取static member 就会被内部转化为对该唯一的extern 实体的直接参考操作。若取一个static data member的地址,会得到一个数据类型的指针,而不是只想起class member的指针。
nonstatic data members:
只要程序猿在一个成员函数中直接处理一个非静态成员变量,“隐式类对象(this指针)”就会出现。
欲对一个nonstatic data member 进行存取操作,编译器需要把this指针(class object的起始地址)加上data member的偏移量。
非静态成员变量的偏移量在编译时期就可以获知,即使这个成员变量是属于被派生的类。因此存取效率高。
3.4 继承 与 Data Member
class Point2d{
public:
//....
private:
float x,y;
}
class Point3d{
public:
//....
private:
float x,y,z;
}
讨论上述结构,与 “提供继承结构” 有什么不同?
下面分四种情况讨论。
1.只要继承不要多态
具体继承(相对于虚拟继承)并不会增加空间或存储时间上的额外负担。这种情况base class和derived class的objects都是从相同的地址开始,其差异只在于derived object 比较大,用以容纳自建的静态数据成员,把一个derived class object指定给base class 的指针或引用(一定要通过指针或引用),并不需要编译器去调停或修改地址,提供了最佳执行效率。
2.加上多态
即在继承关系中,提供一个虚函数接口。
多态带来了程序弹性,但这种弹性会带来空间和存取时间的额外负担:
- 导入一个虚函数表 ,用来存储它所声明的每一个virtual functions的地址。
- 在每一个class object中导入一个vptr,提供执行期的链接,使每一个object能够找到相应的virtual table。
- 加强constructor,使它能够为vptr设定初始值,让它指向class 所对应的virtual table 。
- 加强destructor,使它能够消抹“指向class 相关virtual table”的vptr。
3.多重继承
class point2d{
public:
//....
protected:
float x,y;
}
class Vertex{
public:
//....
protected:
Vertex *next;
}
class Vertex3d: public point2d, public Vertex{
//point2d是第一个base class,Vertex是第二个
public:
//....
protected:
float mumble;
}
对于一个多重派生对象,将其地址指定给“最左端(第一个)base class的指针”,情况和单一继承时相同,因为二者都指向了相同的起始地址;至于第二个或后面的base class 的地址指定操作,则需要将地址修改过:加上(<u>或减去,如果是downcast</u>)介于中间的base class subobject(s)的大小。
如果要存取第二个(或后面)的base class 中的一个data member ,会增加额外的成本吗?不需要付出额外的成本,因为members的位置在编译时期就固定了,因此存取member只是一个简单的offset的运算。
4.虚拟继承
这一部分比较难,并且各个编译器实现方式不同。
class如果含有一个或多个 virtual base class subobjects 将被分割为两部分:一个不变区域和一个共享区域。
- 不变区域中的数据,不管后继如何衍化,总是能有固定的offset,这部分可以被直接存取;
- 至于共享区域,所表现的就是virtual base class subobject ,这个部分数据,其位置因为每次派生操作而有变化,所以只能间接存取。