C++对象内存布局(先看总结)

文章目录

  • 一、不存在virtual的对象和继承结构
  • 二、基类包含virtual的单继承结构
        • 2.1、派生类不覆盖虚函数的情况
        • 2.2、派生类覆盖虚函数的情况
  • 三、基类中包含virtual的多继承结构
        • 3.1、派生类中未覆盖基类中的虚函数
        • 3.1、派生类中覆盖了基类中的虚函数
  • 四、虚继承下单继承结构
        • 4.1、派生类未覆盖基类的虚函数
        • 4.2、派生类覆盖了基类的虚函数
  • 五、typeid的使用方法
  • 六、总结
  • 七、最后看一道题


一、不存在virtual的对象和继承结构

 
这种情况没什么好说的,继承情况下对象会将基类对象的所有成员都继承过来,只是private成员不可访问。
 

二、基类包含virtual的单继承结构

2.1、派生类不覆盖虚函数的情况

 

class A {
    virtual void func() {}

    int ma;
};

class B : public A {
    int mb;
};

 
对象的内存布局如下图所示:
 
C++对象内存布局(先看总结)_第1张图片
vtable内容如下图所示:
 
C++对象内存布局(先看总结)_第2张图片

基类中有virtual函数,所以A中多了一个vtable,B中将A中所有的内容继承下来,再加上自己的成员。
 

2.2、派生类覆盖虚函数的情况

 

class A {
    virtual void func() {}

    int ma;
};

class B : public A {
	virtual void func() {}
    int mb;
};

对象的内存布局不变,因为成员变量和继承结构没有改变。
vtable的内容如下图所示:
 
C++对象内存布局(先看总结)_第3张图片
 

三、基类中包含virtual的多继承结构

3.1、派生类中未覆盖基类中的虚函数

class A {
    virtual void funcA() {}

    int ma;
};

class B {
    virtual void funcB() {}

    int mb;
};

class C : public B, public A {
    int mc;

    void func() {}
};

对象的内存布局如下图所示:
C++对象内存布局(先看总结)_第4张图片
很好理解,按照继承的顺序将基类中的成员变量和vtable pointer继承过来,vtable内容如下图所示:

派生类的vtable会先将第一继承的基类的虚表拿过来,然后在RTTI中留下信息,然后再将第二继承的基类虚表拿过来。
 

3.1、派生类中覆盖了基类中的虚函数

class A {
    virtual void funcA() {}

    int ma;
};

class B {
    virtual void funcB() {}

    int mb;
};

class C : public B, public A {
    int mc;

    void funcA() {}

    void func() {}
};

对象的内存布局没有变化,因为成员变量和继承结构没有发生变化,vtable内容如下图所示:

注意派生类的虚函数表中两个虚函数是放在一块儿的。
 

四、虚继承下单继承结构

4.1、派生类未覆盖基类的虚函数

class A {
    virtual void funcA() {}

    int ma;
};

class B : virtual public A {
    virtual void funcB() {}

    int mb;
};

对象内存布局如下图所示:
C++对象内存布局(先看总结)_第5张图片
此时派生类有了自己单独的vtable pointer,vtable内容如下图所示:
C++对象内存布局(先看总结)_第6张图片
 

4.2、派生类覆盖了基类的虚函数

对象内存布局不变,因为继承结构和成员变量没有发生改变,
vtable内容如下图所示:

 

五、typeid的使用方法

typeid(ptr).name()方法传递指针或者对象,如果传递给方法的是指针,则name()输出的结果是静态绑定的结果(编译的时候就确定),如果传递给方法的是指针解引用,name()输出的结果是动态绑定的结果(RTTI),typeid使用方法如下:


#include 
#include 
//using polymorphic base class B1
class B1 {
public:
    virtual void fun() {}
};
//using non-polymorphic base class B2
class B2 {};
class D1 : public B1 {};
class D2 : public B2 {};
using namespace std;
//main function
int main() {
    D1* d1 = new D1;
    B1* b1 = d1;
    D2* d2 = new D2;
    B2* b2 = d2;
//Printing the type of above class objects on the console
    cout << typeid( d1 ).name() << endl;
    cout << typeid( b1 ).name() << endl;
    cout << typeid( *d1 ).name() << endl;
    cout << typeid( *b1 ).name() << endl;
    cout << typeid( d2 ).name() << endl;
    cout << typeid( b2 ).name() << endl;
    cout << typeid( *d2 ).name() << endl;
    cout << typeid( *b2 ).name() << endl;
}

 

六、总结

 
前面挂了这么多图片看的头晕眼花,更易读的内容看这里吧。

  1. 单继承中,派生类的对象会把基类的虚表拿过来,然后合并,其中RTTI中包含了基类和派生类的信息,此时vtable中的函数一定只有基类中未被覆盖的函数和派生类中覆盖基类的函数。
  2. 多继承中,只有继承的第一个基类的虚表会和派生类融合(和单继承中融合的方式相同),其余的基类不会融合,而是有自己单独的虚表项,此时整个vtable中一定只有基类中未被覆盖的函数和派生类中覆盖基类的函数。
  3. 虚继承中,派生类的虚表和基类的虚表不会融合到一块儿,而且重复继承的表会自动去重,比如菱形继承中的对象内存分布。

总之,派生类对象内存中的虚表一定只有基类中未被覆盖的函数和派生类中覆盖基类的函数。

 

七、最后看一道题

C++对象内存布局(先看总结)_第7张图片
 
如上题目中代码产生的对象内存布局见下图,主要看指针:
C++对象内存布局(先看总结)_第8张图片

你可能感兴趣的:(coding,c++,开发语言)