深度探索C++ 对象模型 笔记 - 第三章
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运行结果:
class A:virtual public X{};
class B: virtual public X{};
class C: public A,public B {};
sizeof运行结果:
对比上述两种继承方式中在内存中的大小分布.首先第一种普通继承方式下仅仅是继承了基类中隐藏的byte.
而第二种虚继承中,A、B、C三者的大小受到三个因素的影响:
1、 语言本身所造成的额外负担(重点)
在编译器中,由于vitrual base class在多重继承中会折叠成为一个由最后派生的class 维护的单一子类,同时还需要支持能够在base 指针和 derived 指针之间转换的多态操作.
所以在每一个virtual 继承而来的derived class object 中安插一些指针,每个指针指向一个virtual base class .通过指针的间接操作来完成对virtual base class 共享操作.
但是这样会出现两个问题:
- 当virtual继承多了这些指针也增多了
- 随着虚拟继承串链的增长,间接存取的深度也增加了.
第一个问题:
A.引入虚继承基类表(微软)
B. 将虚继承基类的偏移量存放在虚函数表的首部.
第二个问题:通过拷贝操作取得所有嵌套指针来对指针的数量进行压缩
2、 编译器对特殊情况的优化处理
3、 Alignment的处理
在大部分的机器上,聚合的结构体大小都会收到aligment的限制,使他们能够在更有效率的在内存中被存取.
另一个作用就是图中所说
了解了上述三个因素后我们就能够对A.B.C三个类的大小做出一个很好的理解了
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;