菱形继承

菱形继承
在谈完单继承模型和多继承模型之后,我们来进一步了解更复杂的继承模型——菱形继承
菱形继承又叫钻石继承。主要结构如图:

菱形继承类图示

菱形继承中含有单继承和多继承。由于class B 和class C都继承了class A中的共有成员_a。那么在class D 多继承时就了存在二义性和数据冗余的问题,虚继承为解决此问题而存在。所以,菱形继承又多出来一种特例——含有虚继承的菱形继承。
首先我们来看一般的菱形继承:

class A
{
public:
    virtual void f1()
    {
        cout << "A::f1()" << endl;
    }
    virtual void f2()
    {
        cout << "A::f2()" << endl;
    }
public:
    int _a;
};
class B:public A
{
public:
    virtual void f1()
    {
        cout << "B::f1()" << endl;
    }
    virtual void f3()
    {
        cout << "B::f3()" << endl;
    }
public:
    int _b;
};
class C:public A
{
public:
    virtual void f1()
    {
        cout << "C::f1()" << endl;
    }
    virtual void f4()
    {
        cout << "C::f4()" << endl;
    }
public:
    int _c;
};
class D:public B,public C
{
public:
    virtual void f1()
    {
        cout << "D::f1()" << endl;
    }
    virtual void f5()
    {
        cout << "D::f5()" << endl;
    }
public:
    int _d;

};
typedef void(*V_FUNC)();//
void PrintVtable(int* vtable)
{

    printf("Vtable:0x%p\n", vtable);
    int **pptable = (int**)vtable;
    for (size_t i = 0; pptable[i] != 0;++i)
    {
        printf("pptable[%u]:0x%p  ", i, pptable[i]);
        V_FUNC f = (V_FUNC)pptable[i];//
        f();//
    }
    cout << "------------------------------------------"<<endl ;
}
void Test1()
{
    D d;
    d.B::_a = 1;
    d._b=2;
    d.C::_a=3;
    d._c = 4;
    d._d = 5;

    PrintVtable(*((int**)&d));
    PrintVtable(*((int**)((char*)&d + sizeof(B))));
    //PrintVtable(*((int**)((char*)&d + sizeof(B))));

}
int main()
{
    Test1();
    system("pause");
    return 0;
}

由于调试中的监视窗口无法准确看到虚函数的实际调用。 我们 通过调用一个打印虚表函数来直观看实际调用的虚函数。内存中对虚表地址也有体现。下面图示让你豁然开朗。
菱形继承模型

趁热打铁,我们来说说菱形虚继承
首先虚继承的关键字 virtual和虚函数关键字相同,但是没有丝毫联系。
上面我们提到虚继承是为解决问题而生,那么定义格式如下:
class 子类名:virtual 继承方式 类名
{};

在上面例子中应该给B、C冠以虚继承。
class B : virtual public A
{};
class C : virtual public A
{};
这里写图片描述

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

    PrintVtable(*((int**)&d));
    PrintVtable(*((int**)((char*)&d + sizeof(B)-sizeof(A))));
    PrintVtable(*((int**)((char*)&d + sizeof(D)-sizeof(A))));

}
int main()
{
    Test1();
    system("pause");
    return 0;
}

除了给BC两个类加上关键字之外,测试函数在打印虚表时应注意内存对齐问题。
同样用图示来更直观理解虚函数表。
菱形虚继承模型

总结:
我们要清楚这门语言背着我们在干了什么。需要熟悉这门语言,我们就必需要了解C++里面的那些东西,需要我们去了解他后面的内存对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值