转载: C++内存排列与寻址
struct - 简单的内存布局原则:成员变量按其被声明的顺序排列,按具体实现所规定的对齐原则在内存地址上对齐
class - C++ 为了实现虚函数和虚继承引入的隐藏成员变量外,C++类实例的大小完全取决于一个类及其基类的成员变量!成员函数基本上不影响类实例的大小
C++ 标准委员会不限制由“public/protected/private”关键字分开的各段在实现时的先后顺序的变量,因此,不同的编译器实现的内存布局可能并不相同
static int bsm不占用内存空间,因为它是静态成员,该数据存放在程序的数据段中,不在类实例中
单继承 - 在单继承类层次下,每一个新的派生类都简单地把自己的成员变量添加到基类的成员变量之后, 派生类D的指针,即基类C的指针, 不必要计算偏移量
多继承 - 与单继承不同的是,在多重继承下,内嵌的两个基类的对象指针不可能全都与派生类对象指针相同。 VC++ 按照基类的声明顺序先排列基类实例数据,最后才排列派生类数据
虚继承 - 1 首先排列非虚继承的基类实例;
2 有虚基类时,为每个基类增加一个隐藏的vbptr(全类共享的偏移量表),除非已经从非虚继承的类那里继承了一个vbptr;
3 排列派生类的新数据成员;
4 在实例最后,排列每个虚基类的一个实例
成员变量访问:
没有继承: 没有任何继承关系时,访问成员变量和C语言的情况完全一样:从指向对象的指针,考虑一定的偏移量即可
单继承: 由于派生类实例与其基类实例之间的偏移量是常数0,所以,可以直接利用基类指针和基类成员之间的偏移量关系,如此计算得以简化
多重继承 :虽然派生类与某个基类之间的偏移量可能不为0,然而,该偏移量总是一个常数。只要是个常数,访问成员变量,计算成员变量偏移时的计算就可以被简化。
可见即使对于多重继承来说,访问成员变量开销仍然不大
虚继承: 当类有虚基类时,访问非虚基类的成员仍然是计算固定偏移量的问题。然而,访问虚基类的成员变量,开销就增大了 ,因为必须经过如下步骤才能获得成员变量的地址:
1. 获取“虚基类表指针”;
2. 获取虚基类表中某一表项的内容;
3. 把内容中指出的偏移量加到“虚基类表指针”的地址上。
然而,事情并非永远如此。正如下面访问I对象的c1成员那样,如果不是通过指针访问,而是直接通过对象实例,则派生类的布局可以在编译期间静态获得,
偏移量也可以在编译时计算,因此也就不必要根据虚基类表的表项来间接计算
额外保存一个指向直接虚基类的指针,然后就可以从直接虚基类找到它的虚基类,逐级上推(编译器优化
强制转化:
如果没有虚基类的问题,将一个指针强制转化为另一个类型的指针代价并不高昂。如果在要求转化的两个指针之间有“基类-派生类”关系,编译器只需要简单地在两者之间加上或者减去一个偏移量即可
当继承关系中存在虚基类时,强制转化的开销会比较大, 当从派生类中访问虚基类成员时,应该先强制转化派生类指针为虚基类指针,然后一直使用虚基类指针来访问虚基类成员变量。
这样做,可以避免每次都要计算虚基类地址的开销
成员函数:
Reference: https://blog.csdn.net/zjs342/article/details/44977337

浙公网安备 33010602011771号