Data语意学

概述 : 这部分主要内容是在将Class Object中数据成员在内存中的分布,深入的讲解了在多重继承、虚继承等情况下编译器对于Class模型的构建。同时也对成员对象的使用效率方面做了深入的研究分析.

Class Object内存布局

对于class X{}这样一个对象而言,sizeof(X)后获得的大小为1,它里面包含有一个隐藏的1byte大小,这样可以使得两个object可以在内存中配置独一无二的地址.

class A:public X{};

class B: public X{};

class C: public A,public B {};

sizeof运行结果:

第三章  普通继承.jpg

class A:virtual public X{};

class B: virtual public X{};

class C: public A,public B {};

sizeof运行结果:
对象模型 第三章.jpg

对比上述两种继承方式中在内存中的大小分布.首先第一种普通继承方式下仅仅是继承了基类中隐藏的byte.

而第二种虚继承中,A、B、C三者的大小受到三个因素的影响:

1、 语言本身所造成的额外负担(重点)

在编译器中,由于vitrual base class在多重继承中会折叠成为一个由最后派生的class 维护的单一子类,同时还需要支持能够在base 指针和 derived 指针之间转换的多态操作.

所以在每一个virtual 继承而来的derived class object 中安插一些指针,每个指针指向一个virtual base class .通过指针的间接操作来完成对virtual base class 共享操作.

但是这样会出现两个问题:

  1. 当virtual继承多了这些指针也增多了
  2. 随着虚拟继承串链的增长,间接存取的深度也增加了.

第一个问题:

A.引入虚继承基类表(微软)

B. 将虚继承基类的偏移量存放在虚函数表的首部.

第二个问题:通过拷贝操作取得所有嵌套指针来对指针的数量进行压缩

2、 编译器对特殊情况的优化处理

3、 Alignment的处理

在大部分的机器上,聚合的结构体大小都会收到aligment的限制,使他们能够在更有效率的在内存中被存取.

另一个作用就是图中所说
padding.jpg

了解了上述三个因素后我们就能够对A.B.C三个类的大小做出一个很好的理解了

虚基类.jpg

vptr指针的存放位置

C++ 标准中,vptr指针可以存放在C++ 模型中的任意位置,但是一般来说编译器都要么存放在首部,要么就是尾部。

首部: 对于”在多重继承之下,通过指向class members 调用virtual function “ 会带来一些帮助,否则将会从class object起始点开始亮起.

尾端: 提供对C语言的兼容性.

指向data members 的指针

对于:

class Point3d{
public :
virtual ~Point3d();
….
public:
static Point3d origin;
float x,y,z;
}

取某个坐标成员的地址:

&Point3d::z;

上述操作将得到z坐标在class object 中偏移位置.最低限度是其x+y的大小总和.

注意 : 为了区分一个没有指向任何data member 的指针和一个指向第一个data member ,每一个真正的member offset的值都被加上1

使用:

float Point3d::*ax=&Point3d::x;
Point3d pA;
pA.*ax=2; //这么做效率较低

等价于
pA.ax=2;