您的位置:首页 > 编程语言 > C语言/C++

C++对象模型——Data 语意学(第三章)

2015-08-02 16:35 381 查看

第3章 Data 语意学

计算如下代码的sizeof结果:

class X{};
class Y : public virtual X{};
class Z : public virtual X{};
class A : public Y, public Z{};
上述X,Y,Z,A中没有任何一个 class 内含明显的数据,只表示了继承关系,所以认为每一个 class 的大小都是0.这样想法是错误的.即使 class X的大小也不为0.

一个空的 class 如:

// sizeof(X) = 1
class X{};
事实上它并不是空的,它有一个隐式的1 byte,那是编译器插入的一个char,这使得这个 class 的Object可以在内存中分配唯一的地址:

X a, b;
if (&a == &b)
    cerr << "yipes!" << endl;
Y和Z的大小都是4(在g++4.8上测试),这个大小和机器有关,也和编译器有关.事实上Y和Z的大小受到三个因素的影响:

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

2. 编译器对于特殊情况所提供的优化处理 virtual base class X subobject的1byte也出现在 class Y和 class Z上,传统上它被放在derived class 的固定部分的尾端.某些编译器会对empty virtual base class 提供特殊支持.

3. Alignment限制 在大部分机器上,群聚的结构体大小会受到alignment的限制,使它们能够更有效率地在内存中被存取.alignment将数值调整到4或者8的整数倍.

Empty virtual class已经成为C++ OO设计的一个特有术语,它提供一个 virtual interface,没有定义任何数据,某些新的编译器(例如g++4.8,因为Lippmanz这本书是2000年写的)对此提供了特殊处理.在这个策略下,一个empty virtual base class 被视为derived
class object最开头的部分,也就是说它没有花费任何的额外空间,这就节省了上述第二点的1bytes(因为既然有了members,就不需要原本为了empty class 而插入的一个char),也就不再需要第三点所说的3bytes的填补.只剩下第一点所说的额外负担,在此模型下Y和Z的大小都是4而不是8(早期的编译器为8).


编译器之间的潜在差异说明了C++对象模型的演化,这个模型为一般情况提供解决之道,当特殊情况逐渐被挖掘出来时,种种尝试(尝试错误)于是被引入,提供优化的处理.如果成功,启发的方法就提升为普遍的策略,并跨越各种编译器而合并.它被视为标准,久而久之就成了语言的一部分. virtual function table就是一个例子.另一个例子是第二章介绍的"named return value(NRV)优化"

那么 class A的大小呢?很明显,某种程度上要视编译器而定.一个 virtual class subobject只会在derived class 中存在一份实体,不管它在 class 继承体系中出现了多少次! class A的大小由下列几点决定:

1. 被共享的唯一一个 class X实体,大小为1bytes

2. base class Y的大小,减去"因virtual base class X而配置"的大小,结果是4bytes.Base class Z也是4bytes,加起来是8bytes.

3. class A的alignment数量(如果有的话).前述三项总合,表示调整前的大小是9bytes.class A必须调整至4bytes边界.所以需要填补3bytes,结果是12bytes.

如果考虑"特别对empty virtual base class做了处理"的编译器,base class Y的大小为4bytes, base class Z的大小为4bytes,因此 class A的大小为8bytes.如果在 virtual base class X中存放一个(或以上)的data members,两种编译器("有特殊处理"和"没有特殊处理"的)就会产生出完全相同的对象布局.

当 class X中没有数据,empty base class 不花费额外的空间,对 class X,Y,Z,A求sizeof的结果如下所示:



当 class X中存放一个数据char c;时,结果如下所示(两种编译器结果相同):



一个 class 的data members,一般而言可以表现这个 class 在程序执行时的某种状态.Nonstatic data members放置的是"个别的class object"感兴趣的数据, static data members则放置的是"整个class"感兴趣的数据.

C++对象模型尽量以空间优化和存取速度优化的考虑来表现nonstatic data members,并且保持和C语言 struct 数据配置的兼容性.它把数据直接存放在每一个 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也存在).但是一个 template class 的 static data members的行为稍微不同.

每一个 class object因此必须有足够的大小以容纳它所有的nonstatic data members.有时候可能令人吃惊,因为它可能比想象的还大,原因是:

1. 有编译器自动加上的额外data members,用以支持某些语言特性(主要是各种virtual特性)

2. 因为alignment(边界调整)的需要
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: