【C++】继承和多态之——菱形继承


菱形继承的模型


  菱形继承是c++中的一种多继承,它的继承关系模型如下图,因此被称为菱形继承。

这里写图片描述


菱形继承中的问题


菱形继承中存在的问题

——菱形继承是多继承,那么和单继承不同,可能会出现一些问题:如果Child_1,Child_2都对Father中fun1进行重写,共用一个虚表,此时就会发生冲突。这时就要(virtual)虚函数,这时Child_1,Child_2就会生成各自的虚表,互不影响。


先写一个菱形继承的代码,用此代码来解释关于菱形继承的问题


#include<iostream>
using namespace std;



class  Father
{
public:

    virtual  void fun1()
    {
        cout << "class Father: fun1()" << endl;
    }
    virtual void fun2()
    {
        cout << "class Father: fun2()" << endl;
    }
    int _a;
};

class Child_1:public Father
{
public:
    virtual void fun1()
    {
        cout << "class Child_1: fun1()" << endl;
    }

    virtual void fun3()
    {
        cout << "class Child_1: fun3()" << endl;
    }
    int _b;

};


class Child_2 :public Father
{
public:
    virtual void fun1()
    {
        cout << "class Child_2: fun1()" << endl;
    }
    int _c;

};

class Person :public Child_1, public Child_2
{
public:
    virtual void fun1()
    {
        cout << "class Person: fun1()" << endl;
    }
    virtual void fun2()
    {
        cout << "class Person: fun2()" << endl;
    }

    virtual void fun4()
    {
        cout << "class Person: fun4()" << endl;
    }

    int _d;
};
//打印虚表内容,以便更直观的看虚表内容
typedef void(*P_TEST)();

void PrintVtable(int* vtable)
{
    printf("Vtable 0x%p\n", vtable);
    int ** ppVtable = (int**)vtable;
    for (size_t i = 0; ppVtable[i] != 0; i++)
    {
        printf("vtable[%u]: 0x%p->", i, ppVtable[i]);
        P_TEST test = (P_TEST)ppVtable[i];
        test();

    }
}

void FunTest()
{
    Person p;
    p.Child_1::_a = 1;
    p.Child_2::_a = 2;
    p._b = 3;
    p._c = 4;
    p._d = 5;
    PrintVtable(*(int**)&p);

}

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


运行结果截图 :

图一
这里写图片描述

为了更深入理解虚表,我们可以结合编译器内存来对比>:

——图一中,地址0x00aedb18 和地址0x00aedb30分别指向child_1的虚表和child_2的虚表,是两个虚表指针。

图二
这里写图片描述

可以看出,虽然Child_1和Child_2都继承了Father类,但是它的虚表有两份,Person对Child_1中fun1()重写时并没有影响Child_2中fun1的内容。


虚继承如何解决数据的冗余和二义性问题


多继承中存在的问题

———多继承时每个派生类都会继承基类的数据,这时,就会出现多份重复的数据,产生数据的冗余和二义性问题,所以为了不浪费空间,我们采用虚继承的方式继承,存这些数据成员的表叫做虚基表->图四。


将上面的代码稍作修改,改为虚继承
————注意:虚函数,虚继承都是加 virtual,但每个意义不同,注意理解!

//加virtual关键字
class Child_1:virtual public Father...
class Child_2:virtual public Father...
...
PrintVtable(*(int**)&p);
    PrintVtable(*(int**)((char*)&p+sizeof(Child_1)-sizeof(Father)));
    PrintVtable(*(int**)((char*)&p+sizeof(Person)-sizeof(Father)));

图三

图四
这里写图片描述

对比虚继承和没有虚继承的区别(第一个代码不是虚继承):

  1. 成员:虚继承只有一份Father类成员,哪个派生类需要访问就对数据重写,而没有虚继承时,每个派生类都有基类成员
  2. 虚表:虚继承比没有虚继承多一个虚表,多出的就是一份基类的虚函数,在需要时对它重写,而没有虚继承时,每个派生类的虚表中都包括一份基类虚函数,在需要时重写。(虚继承的打印结果多出来一个地址,并不是虚表地址,它是存了一个偏移量,用于派生类访问基类成员
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值