c++对象内存管理

1.基础


  每个对象有独立的数据成员(非static),但是内存中成员函数只有一份,该类的所有对象共享成员函数。

  static数据成员属于类,该类的所有对象共享static数据成员,static数据成员存储在静态存储区。对象数据成员依据创建对象的方式不同,可能存在于栈上或者堆上。成员函数存储在代码段。

  那成员函数怎么识别是哪个对象调用的?编译器在编译阶段,进行函数的重构,通过将this指针作为函数的第一个参数找到调用该函数的对象。

2.有继承关系的对象内存布局


  派生类要保留基类的所有属性和行为,因此每个派生类的实例都包含了一份完整的基类实例数据。

  [1]单继承时,虚函数表在最前面的位置;成员变量根据其继承和声明顺序依次存放;被重写的虚函数地址在虚表中进行更新。

  [2]多继承时,每个基类的自身成员不变(每个基类都有自己的vptr);子类的虚函数被放到了第一个父类的虚表中;基类按照继承顺序在内存中存放;被重写的虚函数地址在每个基类的虚表中进行更新。

  [3]重复继承(钻石继承)时,即派生类D继承了两个基类B1、B2,同时B1、B2都继承了基类B。此时由于基类B1、B2的内存内容不变,所以B的成员在D中有两份(一份是B1继承而来的,另一份是B2继承而来的),所以用D的对象给B的成员变量赋值会产生二义性编译错误。

  [4]菱形继承(虚拟继承),即派生类D继承了两个基类B1、B2,同时B1、B2都虚拟继承了基类B(想比上面那一条继承B时只是使用了virtual关键字)。此时使用virtual修饰的基类只有一个共享对象,此时派生类会把其独立的部分放在前面,共享部分放在后面。

  结合虚表结构和对象的内存布局,此时可以明白,当把派生类的指针转换成基类指针时编译器可以直接根据对像的内存布局进行偏移,从而指向基类的内存起始处(vptr),查找虚表后调用里面的虚函数。

       

3.问题


  问题1:派生类D继承了两个基类B1、B2,同时B1、B2都继承了基类B,使用D的对象给基类B的成员变量赋值:

D d;
d.ib = 0;         //二义性错误 , ib是B的成员变量
d.B1::ib = 1;     //正确
d.B2::ib = 2;     //正确

  

posted on 2019-03-19 15:30  能量星星  阅读(499)  评论(0编辑  收藏  举报

导航