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

根据几篇博客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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值