c++对象模型

from 深入浅出c++对象模型

c++对象模型

C++类具有的结构 data member(分为static和 non-static) function member( static non-static virtual)

static data static function non-static function 存放在对象之外

non-static data 和 virtual function 存入对象内

在此模型下,nonstatic 数据成员被置于每一个类对象中,而static数据成员被置于类对象之外。static与nonstatic函数也都放在类对象之外,而对于virtual 函数,则通过虚函数表+虚指针来支持。

vptr是虚函数 虚函数指针指向虚函数表,虚函数表中存储的是一系列虚函数的地址,
虚函数地址出现的顺序与类中虚函数声明的顺序一致。

对象只包含非静态成员变量和vptr 虚函数指针 指向类生成的虚表。

c++继承的对象模型示意图

单继承的 C++模型

基类指针(bptr),基类指针指向指向一个基类表(base class table),同样的,由于间接性导致了空间和存取时间上的额外负担,优点则是无须改变子类对象本身就可以更改基类。表格驱动模型的图就不再贴出来了。

单继承只有一个vptr

在C++对象模型中,对于一般继承(这个一般是相对于虚拟继承而言),若子类重写(overwrite)了父类的虚函数,则子类虚函数将覆盖虚表中对应的父类虚函数(注意子类与父类拥有各自的一个虚函数表);若子类并无overwrite父类虚函数,而是声明了自己新的虚函数,则该虚函数地址将扩充到虚函数表最后(在vs中无法通过监视看到扩充的结果,不过我们通过取地址的方法可以做到,子类新的虚函数确实在父类子物体的虚函数表末端)。而对于虚继承,若子类overwrite父类虚函数,同样地将覆盖父类子物体中的虚函数表对应位置,而若子类声明了自己新的虚函数,则编译器将为子类增加一个新的虚表指针vptr。

类之间关系如下:

对象模型如下:

多继承下的C++模型

多重继承关系如下:

对象模型如下

构造函数

被合成的默认构造函数只满足编译器的需求 而不是程序的需求

有四种情况使用被合成的默认构造函数
(1)带有默认构造函数的成员对象变量
(2)带有默认构造函数的基类 子类调用基类默认构造函数
(3)带有virtual function的类
(4)带有virtual base class 的类

在默认构造函数中 只有基类的对象成员 和 自身的对象成员变量会被初始化
其它的non-static 变量 如整数 指针不会被初始化

拷贝构造函数在编译器需要时生成 使用默认成员拷贝 对成员变量逐个拷贝 bitwise copy 位逐次拷贝
拷贝构造使用的情况
(1) 用于第一次作为初值进行初始化 X x; X xx=x;
(2) 函数传递 值传递
(3) 函数返回值

返回值的初始化 传入返回值的引用 调用两次构造函数
举例:

优化后结果 返回结果为构造函数 少调用一次构造函数

使用成员初始化列表的情况

(1) 初始化引用变量

(2) 初始化const 成员

(3) 调用基类的构造函数 构造函数有参数

(4) 调用对象成员的构造函数 构造函数有参数

使用成员初始化列表的初始化顺序与 列表顺序无关 与类中变量定义顺序相关

Data语意学

绑定

空对象 sizeof为1 编译器插入一个char数据 使得不同的空对象地址不同

字节对齐 32位机器上是4个字节

例子如下所示
继承关系:

对象占用内存情况

对象大小:

data member的绑定 将嵌入的类型声明放入 class的起始位置 防止全局变量的影响

布局

在同一个access section中(也就是 private protected public等区段)中 成员变量的排序顺序符合较晚出现的成员变量出现在较高的地址上 考虑到字节对齐

data member的继承

vptr放在class object的前端或尾端 视编译器而定

多重继承

虚拟继承 支持相同的基类只有一份


不变局部和共享局部 不变局部是无论后续如何衍化 通过固定的offset可以存取这些数据 共享局部就是virtual base class subobject 即相同的部分数据 随着派生操作而变化

解决策略 为派生类增加指向虚基类的指针 为了解决多层次继承指针多次寻找问题 可以直接存储offset

Function语意学

构造 解构 拷贝 语意学

执行期 语意学

站在对象模型的尖端