C++的继承,多继承,虚继承的对象分布的总结

来源:互联网 发布:什么软件收二手手机 编辑:程序博客网 时间:2024/06/05 11:10

根据几篇博客C++对象模型,c++涉及继承和虚继承时的内存布局 ,C++ 对象的内存布局(上) 作了一些归纳和总结,留着备用吧。
讨论问题:

  • 单继承内存布局?
  • 单继承(虚继承,virtual继承)的布局?
  • 多继承内存布局?
  • 虚继承内存布局?

1、单继承(非虚继承)

先看单继承的例子:

#include<iostream>using namespace std;class Parent{    public:        int age;        Parent():age(10){}        virtual void f(){cout << "Parent::f()" << endl;}        virtual void g(){cout << "Parent::g()" << endl;}};class Child : public Parent{    public:        int num;        Child():num(6){}        virtual void f(){cout << "Child::f()" << endl;}        virtual void h(){cout << "Child::h()" << endl;}        virtual void gc(){cout << "Child::gc()" << endl;}};class GrandChild : public Child{    public:        int gpa;        GrandChild() : gpa(4){}        virtual void f(){cout << "GrandChild::f()" << endl;}        virtual void ggc(){cout << "GrandChild::ggc()" << endl;}        virtual void gc(){cout << "GrandChild::gc()" << endl;}};

单继承的内存分布:
单继承对象分布

单继承(非虚继承)基本上继承父类的 vptr,并且会将自己的类的虚函数直接添加在子类的vptr,是一种 “is A ”的关系,可以共用vptr,而且对象成员变量也相应的继承过来。

2、多继承

using namespace std;class B{   public:        int ib;        char cb;    public:        B():ib(0),cb('B') {}        virtual void f() { cout << "B::f()" << endl;}        virtual void Bf() { cout << "B::Bf()" << endl;}};class B1 : virtual public B{    public:        int ib1;        char cb1;    public:        B1():ib1(11),cb1('1') {}        virtual void f() { cout << "B1::f()" << endl;}        virtual void f1() { cout << "B1::f1()" << endl;}        virtual void Bf1() { cout << "B1::Bf1()" << endl;}};class B2: virtual public B{    public:        int ib2;        char cb2;    public:        B2():ib2(12),cb2('2') {}        virtual void f() { cout << "B2::f()" << endl;}        virtual void f2() { cout << "B2::f2()" << endl;}        virtual void Bf2() { cout << "B2::Bf2()" << endl;}};class D : public B1, public B2{    public:        int id;        char cd;    public:        D():id(100),cd('D') {}        virtual void f() { cout << "D::f()" << endl;}        virtual void f1() { cout << "D::f1()" << endl;}        virtual void f2() { cout << "D::f2()" << endl;}        virtual void Df() { cout << "D::Df()" << endl;}};

多继承的对象分布:
这里写图片描述

多继承也基本类似单继承,只不过多了几张虚函数表,位置是根据多继承声明的顺序。第一张虚表还安插自己类的虚函数。每个表后面就是继承子类的成员变量。很明显,如果多继承的基类有共同的 “基基类”,必然就有2份“基基类”成员
这里写图片描述


3、虚继承

如果上面的多继承的基类(B1,B2)虚继承B,那么分布就是如下:
这里写图片描述

从图上可以看出,多了额外的一张虚表 就是 “基基类”的虚表,虚继承是一种“has A”的关系,不是is-a关系,会保存自己的一张虚表和成员,后续派生的数据成员也是只有一份。
注意:VS上,在B1和B2的 vptr和成员之间位置,会添加虚基类指针vbptr(可能是偏移地址或者直接地址),而clang和GCC没有,其他编译器实现不清楚,有些编译器可能将虚基类的偏移地址 安插到虚函数表指针中(正偏移为虚函数,负为虚基类)

虚继承B1和B2的对象分布

这里写图片描述

这里对于虚线框处,inside object C++ model 说对于虚基类会有一个 基类表指针,指向虚基类的偏移或者虚基类的地址,这里本人在实验的时候,并没有发现 vbptr,vs似乎会生成在vptr和成员变量之间。
棕色部分根据编译器实现而定,vs上似乎会生成(放在vptr和成员变量之间),gcc和clang没有这部分。

注意:VS上,在B1和B2的 vptr和成员之间位置,会添加虚基类指针vbptr(可能是偏移地址或者直接地址),而clang和GCC没有,其他编译器实现不清楚,有些编译器可能将虚基类的偏移地址 安插到虚函数表指针中(正偏移为虚函数,负为虚基类)


测试代码:
机器在64位:指针为8bytes,而且按8bytes对齐方式

#include <iostream>using  namespace std;class Base{public:    virtual void f(){cout<<"Base::f()";}    virtual void g(){cout<<"Base::g()";}    virtual void h(){cout<<"Base::h()";}    long b=1;};class Base2:virtual Base{public:    virtual void f(){cout<<"Base2::f()";}    virtual void g(){cout<<"Base2::g()";}    virtual void gb2(){cout<<"Base2::gb2()";}    long b=2;};class Base3:virtual Base{public:    virtual void f(){cout<<"Base3::f()";}    virtual void g(){cout<<"Base3::g()";}    virtual void gb3(){cout<<"Base3::gb3()";}    long b=3;};class Derive:public Base2,public Base3{public:    virtual void f(){cout<<"Derive::f()";}    virtual void g(){cout<<"Derive::g()";}    virtual void gb4(){cout<<"Derive::gb4()";};    long b=20;};int main() {    //shit  这里是有8字节对齐,int类型存放也是8字节对齐/*    using  Fun= void (*)();    Fun fun= nullptr;    Derive d;    cout<< "虚函数表地址:"<<(long *)&d<<endl;    cout<<"虚表-第一个函数地址:"<<(long*)*(long*)&d<<endl;    //int* b=(int*)((long*)&d+3);  //b 地址(如果是int会进行补齐)    long*b=(long*)&d+1;    cout<<*b<<" "<<*(b+1)<<endl;    long **b2=(long**)((long*)&d+2);//函数表地址    fun=(Fun)b2[0][2];    fun();*/    using  Fun= void (*)();    Fun fun= nullptr;    Base2 b2;    long*b=(long*)&b2+3;    //cout<<*b<<" "<<*(b+1)<<endl;    long** f=(long**)((long*)&b2 + 4);    cout<< sizeof(b2);    return 0;}

1 0
原创粉丝点击