【C++】虚基表和虚表

1.虚基表
(1)菱形继承
【C++】虚基表和虚表_第1张图片
在菱形继承中,类D有两个_a,一个是B类从A类继承而来的,一个是C类从A类继承而来的,在D类赋值时必须指定是对哪个 _a赋值,否则会出现数据不确定问题。
所以菱形继承会造成数据的二义性

//菱形继承
class A
{
public:
      int _a;
};
class B:public A
{
public:
      int _b;
};
class C :public A
{
public:
      int _c;
};
class D :public B, public C
{
public:
      int _d;
};
int main()
{
      D d;
      d.B::_a = 1;
      d.C::_a = 2;
      d._b = 3;
      d._c = 4;
      d._d = 5;
      return 0;
}

【C++】虚基表和虚表_第2张图片

(2)解决菱形继承的数据二义性问题
用虚继承(关键字:virtual)解决数据二义性的问题
是在类B/C继承A的时候为虚继承

class A
{
public:
      int _a;
};
class B:virtual public A
{
public:
      int _b;
};
class C :virtual public A
{
public:
      int _c;
};
class D :public B, public C
{
public:
      int _d;
};
int main()
{
      D d;
      d._a = 1;
      d._b = 3;
      d._c = 4;
      d._d = 5;
      return 0;
}

【C++】虚基表和虚表_第3张图片

(3)探索虚继承是如何实现的
【C++】虚基表和虚表_第4张图片

2 虚表
一个例子,算一下类b的大小

class Base
{
public:
      virtual void f1()
      {
            cout << "Base::f1()" << endl;
      }
      int _base;
};
int main()
{
      Base b;
       b._base = 1;
      cout << sizeof(b) << endl;   // 8
      return 0;
}

我们可以看到并不是只有一个成员变量_base的大小,而是还多4个字节,这4个字节是什么呢?
可以看到这4个字节存放了一个void**类型的指针_vfptr,该指针就是虚表指针
虚表指针指向虚函数表,该表存放的就是类Base中的虚函数的地址。
这里写图片描述
(1)通过虚表探究一下多态时对象的模型:

class Base
{
public:
      virtual void f1()
      {
            cout << "Base::f1()" << endl;
      }
      int _base;
};
class Derived :public Base
{
public:
      virtual void f1()
      {
            cout << "Derived::f1()" << endl;
      }
      int _derived;
};
int main()
{
      Base b;
      Derived d;
      b._base = 1;
      d._base = 2;
      d._derived = 3;
      Base* pb = &b;
      pb->f1();
      pb = &d;
      pb->f1();

      return 0;
}

在派生类Derived继承基类Base时,将Base的虚表也继承下来了,当派生类重写虚函数时,虚表里面该函数的内容会被覆盖
构成多态时的情况是pb指针指向哪里,就调用哪个的函数
所以我们用虚表来解释就是,pb指针指向b,就是b的虚表,调用的也就是b虚表里面的虚函数;pb指针指向d,就是d的虚表,调用的也就是d虚表里面的虚函数。
下面是上述代码运行过程中的现象:
【C++】虚基表和虚表_第5张图片

这里写图片描述

(2)多继承模式下的对象模型

class Base1
{
public:
      virtual void f1()
      {
            cout << "Base1::f1()" << endl;
      }
      int _base1;
};
class Base2
{
public:
      virtual void f1()
      {
            cout << "Base2::f1()" << endl;
      }
      int _base2;
};
class Derived :public Base1,public Base2
{
public:
      virtual void f1()
      {
            cout << "Derived::f1()" << endl;
      }
      virtual void f2()
      {
            cout << "Derived::f2()" << endl;
      }
      int _derived;
};

对于派生类Derived,从Base1继承一个虚表,从Base2继承一个虚表,在派生类重写f1()函数时,将两个虚表内的f1()全部覆盖为Derived::f1();
对于派生类自己的虚函数f2(),放在先继承的类(Base1)中。
【C++】虚基表和虚表_第6张图片

你可能感兴趣的:(c++)