1 虚基表
(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;
}
(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;
}
(3)探索虚继承是如何实现的
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虚表里面的虚函数。
下面是上述代码运行过程中的现象:
(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)中。