语意学

空类的大小:

class X {};
class Y : public virtual X {};
class Z : public virtual X {};
class A : public Y, public Z {};

一个空的class,如:

// sizeof X == 1

事实上并不是空的,它有一个隐晦的1byte,是被编译器安插进去的一个char,使得这个class的两个objects得以在内存中配置独一无二的地址:

X a, b;
if (&a == &b) cerr << "yipes" << endl;

Y,Z的大小受到三个因素的影响:

(1)语言本身所造成的额外负担,当语言支持virtual base classes时,就会导致一些额外的负担。在派生类中,这个额外的负担反映在某种形式的指针身上,或者指向virtual base class subobject或者指向一个相关表格:表格中存放的若不是virtual base class subobject的地址,就是其偏移量。

(2)编译器对特殊情况提供的优化处理

(3)Alignment的限制

一个virtual base class subobject 只会在derived class中存在一份实体,不管它在class继承体系中出现了多少次。

C++对象模型尽量以空间优化和存取速度优化的考虑来表现nonstatic data members。把数据直接存放到每一个class object之中。对于继承而来的nonstatic data members(不管是virtual或nonvirtual base class)也是如此。对于static data members,则被放置在程序的一个global data segment中,不会影响个别class object的大小。在程序中,不管该class被产生多少个objects,static data members永远只存在一份实体(即使该class没有任何object实体,其static data members也已存在)。

1.Data Member的布局

Nonstatic data members 在 class object中的排列顺序和其被声明的顺序一样,任何中间介入的static data members都不会被放进对象布局中。在同一个access section中,members的排列只需符合"较晚出现的members在class object中有较高的地址"。各个members并不一定得连续排列。C++ Standard也允许编译器将多个access section之中的data membeers自由排列,不必在乎它们出现在class声明中的次序。

2. Data Member的存取

Static data members被视为一个global变量,每一个member的存取,以及与class的关联,并不会导致任何空间上或执行时间上的额外负担。每一个static data member只有一个实体,存放在程序的data segment之中,每次程序取用static member,就会被内部转化为对该唯一的extern实体的直接操作。

若取一个static data member的地址,会得到一个指向其数据类型的指针,而不是一个指向其class member的指针,因为static member并不在一个class object之中。

Nonstatic data members直接存放在每一个class object之中。欲对一个nonstatic data member进行存取操作,编译器需要把class object的起始地址加上data member的偏移量。

指向data member的指针,其offset总是被加上1,这样可以使编译系统区分出“一个指向data member的指针,用以指出class的第一个member”和“一个指向data member的指针,没有指出任何member”两种情况。

每一个nonstatic data member的偏移量在编译时期即可获知,甚至member属于一个base class subobject也是一样。因此,存取一个nonstatic data member,其效率和存取一个C struct member或一个nonderived class的member是一样的。

Point3D origin, *pt = &origin;

origin.x = 0.0;

pt->x = 0.0;

这两种存取方法有什么不同?

当Point3D是一个derived class,而在其继承结构中有一个virtual base class,并且被存取的member,x是一个从该virtual base class继承而来的member时,就会有重大的差异。

因为我们不能够说pt必然指向哪一种class type(因此我们也就不知道编译时期这个member真正的offset位置),所以这个存取操作必须延迟至执行期,经由一个额外的间接索引才能够解决。

但如果使用orgin就不会有此问题,类型已经确定,members的offset位置也在编译时期固定了。

取一个nonstatic data member的地址,将会得到它在class中的offset,取一个真正class object身上的data member的地址,将会得到该member在内存中的真正地址。

原文地址:https://www.cnblogs.com/sssblog/p/13713251.html