“菱形继承与菱形虚拟继承” 

     

      “继承”是c++面向对象语言的特点之一,对于一个类,我们如果想对这个类的功能进行扩充,这就可以通过"继承"的方式重新增添或删除这个类中的某些功能。C++语言支持单继承和多继承,如果不大清楚单继承和多继承的知识,不妨先看一下我以前的博客。正是因为c++支持这两种继承方式,所以才会出现菱形继承的方式。菱形继承顾名思义就是将单继承和多继承进行结合,形成的一种继承结构。下面是简单的菱形继承的图解:

wKioL1b7Gz7iU-v2AAASCrNryzE334.png

        这里有4个类分别是A、B、C、D。B和C都继承类A,我们把父类A也可称为“超类”,而类D又继承了B类和C类,下面是四个类中具体所包含的成员变量和成员函数:


class A
{
public:
    virtual void fun1()
   {
       cout << "A::fun1" << endl;
    }
    virtual void fun2()
   {
       cout << "A::fun2" << endl;
    }
    virtual void fun3()
    {
       cout << "A::fun3" << endl;
    }
private:
    int _a;
};

class B:  public A
{
public:
    virtual void fun1()
    {
        cout << "B::fun1" << endl;
    }
    virtual void fun3()
    {
        cout << "B::fun3" << endl;
    }
private:
    int _b;
};

class C :  public A
{
public:
    virtual void fun1()
    {
       cout << "C::fun1" << endl;
    }
    virtual void fun3()
   {
       cout << "C::fun3" << endl;
   }
private:
    int _c;
};

class D : public B, public C
{
public:
    virtual void fun1()
    {
        cout << "D::fun1" << endl;
    }
    virtual void fun3()
    {
        cout << "D::fun3" << endl;
    }
    virtual void fun4()
    {
        cout << "D::fun4" << endl;
    }
private:
    int _d;
};


      在vs2013上面运行,进行监视能够看出菱形继承里面的内存布局如下:

wKioL1b7Kjjws9rEAABOveFo6AU503.png

      我们能够看到对象d中有继承的类B和类C,其中类B和类C中都各自包括一个虚表指针,且指针的地址是不相同的,则两个类各自存在一个虚函数表。同时我们也能够看到菱形继承中,对象d中存在两个成员_a,这就存在问题,当我们想要访问_a时,编译器也不会知道我们想要访问的是哪一个变量,这就说明菱形继承存在一个“数据冗余”和“二义性“的问题。那么如何解决菱形继承所存在的这种问题呢?


       “虚拟继承”可以解决菱形继承所存在的问题。所谓虚拟继承就是增加一个虚基表指针和偏移量,即就是将类B和类C继承改为虚拟继承:


class B:  virtual  public A
{}
class C :  virtual  public A
{}

      对其进行监视,能够得到d的内存布局如下:


wKioL1b7NmrSAsyWAABL19Dvguw679.png


      从这我们能够明显的看到,菱形虚拟继承比菱形继承的d中多存在一个指针,增加一个指针能够指向偏移量。


       c++支持单继承和多继承,如果稍不注意就会出现菱形继承的问题,所以在写代码时尽量使用单继承。java语言设计的比较好,它只支持单继承,这就避免程序员在写代码时出错。