c++多态对象模型:有虚函数的菱形继承和菱形虚拟继承

本文探讨了C++中的菱形继承和菱形虚拟继承。在菱形继承中,D类拥有两个虚表,而菱形虚拟继承通过消除二义性和数据冗余,使得A类在内存布局中只出现一次。详细分析了虚表的存储和内存布局,揭示了虚继承如何影响对象模型。
摘要由CSDN通过智能技术生成
  • -

一:菱形继承

class A
{
public:
    virtual void func1()
    {
        cout << "A::func1()";
    }

    virtual void func2()
    {
        cout << "A::func2()";
    }
public://这里为public,方便待会赋值查看内存
    int _a;
};

class B : public A
{
public:
    virtual void func1()
    {
        cout << "B::func1()";
    }

    virtual void func3()
    {
        cout << "B::func3()";
    }
public:
    int _b;
};

class C : public A
{
public:
    virtual void func1()
    {
        cout << "C::func1()";
    }

    virtual void func3()
    {
        cout << "C::func3()";
    }
public:
    int _c;
};

class D : public B, public C
{
public:
    virtual void func1()
    {
        cout << "D::func1()";
    }

    virtual void func4()
    {
        cout << "D::func4()";
    }
public:
    int _d;
};

它的结构是这样的:
这里写图片描述
来看他的布局:

void test()
{
    D d;
    d.B::_a = 1;
    d._b = 2;
    d.C::_a = 3;
    d._c = 4;
    d._d = 5;
}

这里写图片描述
可以看出,B继承了A,B里有A的虚表,C里有A的虚表,然后D继承了B和C,那么D里有两个虚表。
这里写图片描述
接下来打印地址:
Print((int**)(((int*)&d)));//第一个虚表
这里写图片描述
可以看到,func4依然是给了第一个虚表。

注:因为清理解决方案导致地址不同,不影响。

这样得到了菱形继承的模型:
这里写图片描述

二:菱形虚拟继承

class A
{
public:
    virtual void func1()
    {
        cout << "A::func1()";
    }

    virtual void func2()
    {
        cout << "A::func2()";
    }
public:
    int _a;
};

class B : virtual public A//虚拟继承
{
public:
    virtual void func1()
    {
        cout << "B::func1()";
    }

    virtual void func3()
    {
        cout << "B::func3()";
    }
public:
    int _b;
};

class C : virtual public A//虚拟继承
{
public:
    virtual void func1()
    {
        cout << "C::func1()";
    }

    virtual void func3()
    {
        cout << "C::func3()";
    }
public:
    int _c;
};

class D : public B, public C
{
public:
    virtual void func1()
    {
        cout << "D::func1()";
    }

    virtual void func4()
    {
        cout << "D::func4()";
    }
public:
    int _d;
};

结构变化不大,就是多了虚拟继承,那么再来看看布局:

void test()
{
    D d1;
    d1.B::_a = 1;
    d1._b = 2;
    d1.C::_a = 3;
    d1._c = 4;
    d1._d = 5;
}

这里写图片描述
好像看不出来什么,好复杂的感觉。
来看内存:
这里写图片描述
因为已经对数据成员赋值,所以看的能清楚一些,蓝色框起来的三个是属于B,桔色框起来三个属于C,紫色框起来三个属于A,接下来我用到打印函数。


    Print((int**)(*((int**)&d1)));
    Print((int**)(*((int**)((char*)&d1 + 12))));
    Print((int**)(*((int**)(    (char*)&d1+28   ))));

这里写图片描述
可以看到,B的虚表存了B的func3和D的func4,C的虚表存了C的func3,A的虚表存了D的func1和A的func2。
之前有文章写了菱形继承,虚拟继承解决了二义性和数据冗余,由于D同时继承了B和C,B和C又分别继承了A,所以D里面会有两份A,所以加上虚拟继承,A会在最下面。
至于虚机表指针:

这里写图片描述
这里写图片描述
这里分别是B和C的虚机表,第一行是-4的意思,表示和虚表的地址差4字节,若没有虚表存在,就会存0。
(ff ff ff ff是-1,顺着推也能得到ff ff ff fc是-4)
第二行是距离A的步数,B离A24字节,C离A12字节。
所以得到了对象模型:
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值