-
占用空间: 空的基类是1字节的大小,由于编译器需要使得不同的对象有不同的地址,以及其他因素,会给空的class一个char,分配空间。如果继承这个类,那么可能会占用这1字节的空间,但新的编译器可能会优化掉,比如在32位机器上,以下两种可能性:
-
-
同名: 外部声明的同名变量,一般会被class内部的变量覆盖。
-
内存中 顺序: non-static data members 在 class object 中的排列顺序将和其被声明的顺序一样。
data的存取:
- static data member 的存取没有什么成本,static对象存放在进程空间数据段固定位置,无论是对象存取还是指针存取,都会转化为className::staticDataName这样的实现。
- 欲对一个 non-static data member进行存取操作,编译器需要把 class object的起始地址加上 data member 的偏移位置。
- 如果是虚继承基类中的data,那么会慢一些,因为会多一层指针操作 。
继承与data member:
-
最简单的无多态的继承对象模型是这样:虽然中间有多个由于字节对齐而产生的空白部分,但是这是必须的,否则在赋值或者切割时,会有不必要的情况发生,比如obj1->obj2,那char bit2的位置就会传入一个随机数,这不是想要的结果。
-
加了多态的C++需要多处理些什么呢??
- 首先,类中如果有virtual functioin, 那么则需要引入一个virtual table,里面存储着虚函数的指针。
- 每个object中都会有一个vptr,用于指向那个table,从而实现动态链接。
- 加强构造函数,让构造函数完成vptr的部分。以及在derived class 的构造函数中,重新设定vptr的值。
- 加强析构函数,完成 vptr 的工作。
-
有多态的单一继承模型:vptr的位置不一定在头部或是尾部,根据编译器而定。
-
无多态、无虚继承的多重继承:继承关系如下
-
而对象模型如下: 按照继承的顺序排列:
-
像上面那样,对像指针转换的时候可能会出问题:由于V3D和第二个继承的Vertex头部没有直接对齐
-
虚拟继承
- 虚拟继承解决了菱形多重继承中有多个重复基类的问题。虚继承会将模型中分为不变区域和共享区域。
- 不变区域中的数据,不管后继如何衍化,总是拥有固定的oet(从 object的开头算起),所以这一部分数据可以被直接存取。至于共享区域,所表现的就是 virtual base class subobject这一部分的数据,其位置会因为每次的派生操作而有变化,所以它们只可以被间接存取。各家编译器实现技术之间的差异就在于间接存取的方法。
- 比如还是类似上面的例子,变成下面这个菱形继承的方式,采用虚基类virtual base class。
- 大部分编译器设计的方法:以指针指向virtual base class(可以参考这个博客https://www.cnblogs.com/zhjblogs/p/14274188.html)
参考下面这两个图解释一下,虚继承的类会以一个vbptr指针的方式存在,指针指出了这个v-base class的偏移。在D类(也就是Vertex3d)中,两个继承来的B和C都包含这样的一个指针,而共享区域的A,会被重新放在D的最下面(而不是重复放在B和C的下面)。这时,两个vbptr指向这个开始位置。这个偏移信息是由一个vbtable来报存。vbtable记录了从这个vptr到这个v-base class的位置偏移(即图中的D::vbtable@B@)。
- 文中还有一个开脑洞的想法,觉得很奇妙!C++之父 Bjarne 喜欢的方法。在虚函数表的负索引的位置,存放v-base的偏移。这样解决了 在类中不需要对每一个v-base都添加一个指针的问题。