首先提出几个问题:
1.为什么要引入虚基类?
通过这个图片来讲解为什么要采用虚基类。其继承关系如上图以及下面的程序所示。
当类C的成员函数调用x时,我们不清楚是通过类B1还是通过类B2来调用的。因为由类A派生出B1和B2时,就会有两个基类A的复制,因而无法确定成员x是从类B1还是类B2继承而来的,产生了二义性。
为了解决这个问题我们引入了虚基类。
虚基类的基本原则是在内存中只有基类成员的一份拷贝。这样,通过把基类继承声明为虚拟的,就只能继承基类的一份拷贝,从而消除歧义。用virtual限定符把基类继承说明为虚拟的。
2.虚基类如何定义的。
class base
{
};
class derived: virtual public base
{
};
如上所示,其定义方式仅仅比普通的派生方式多了一个virtual的修饰词。
3.虚基类的工作方式及构造函数的调用方法。
类C的构造函数分别调用了基类B1和B2的构造函数,但是由于虚基类A在类C中只有一个复制,因此编译器无法确定应该由类B1的构造函数还是类B2的构造函数得到。在这种情况下,C++规定:在执行类B1和B2的构造函数时,都不调用虚基类A的构造函数,而是在类C的构造函数中直接调用了虚基类A的默认构造函数。
另外,关于构造函数我们还比较关心其调用次序,下面我们来介绍其调用的三个原则。
派生类构造函数的调用次序有三个原则。
(1)虚基类的构造函数在非虚基类之前调用;
(2)若同一层次中包含多个虚基类,这些虚基类的构造函数按它们说明的次序调用;
(3)若虚基类由非虚基类派生而来,则仍先调用基类构造函数,再调用派生类的构造函数.
4.附上整个分析的程序代码,供大家使用
- #include <iostream>
- using namespace std;
- class A
- {
- public:
- int x;
- A(int a = 0){x = a; cout<<"调用基类A的构造函数"<<endl;}
- };
- class B1:virtual public A
- {
- public:
- int y1;
- B1(int a = 0, int b = 0):A(b)
- {
- y1 = a;
- cout<<"调用派生类B1的构造函数"<<endl;
- }
- void Print(void)
- { cout<<"B1:x="<<x<<",y1="<<y1<<endl;}
- };
- class B2:virtual public A
- {
- public:
- int y2;
- B2(int a = 0, int b = 0):A(b)
- {
- y2 = a;
- cout<<"调用派生类B2的构造函数"<<endl;
- }
- void Print(void)
- { cout<<"B2:x="<<x<<",y2="<<y2<<endl;}
- };
- class C: public B1, public B2
- {
- public:
- int z;
- C(int a, int b, int d, int e, int m):B1(a,b),B2(d,e)
- {
- z = m;
- cout<<"调用派生类C的构造函数"<<endl;
- }
- void Print()
- {
- B1::Print();
- B2::Print();
- cout<<"z="<<z<<endl;
- }
- };
- void main()
- {
- C c1(100,200,300,400,500);
- c1.Print();
- c1.x = 400;
- c1.Print();
- }
5.下面是一种没有采用虚基类的一种错误方法
- #include <iostream>
- using namespace std;
- class A
- {
- public:
- int x;
- A(int a = 0){x = a; cout<<"调用基类A的构造函数"<<endl;}
- };
- class B1: public A
- {
- public:
- int y1;
- B1(int a = 0, int b = 0):A(b)
- {
- y1 = a;
- cout<<"调用派生类B1的构造函数"<<endl;
- }
- };
- class B2: public A
- {
- public:
- int y2;
- B2(int a = 0, int b = 0):A(b)
- {
- y2 = a;
- cout<<"调用派生类B2的构造函数"<<endl;
- }
- };
- class C: public B1, public B2
- {
- public:
- int z;
- C(int a, int b, int d, int e, int m):B1(a,b),B2(d,e)
- {
- z = m;
- cout<<"调用派生类C的构造函数"<<endl;
- }
- void Print()
- {
- cout<<"x="<<x<<endl;
- cout<<"y1="<<y1<<"y2="<<y2<<endl;
- cout<<"z="<<z<<endl;
- }
- };
- void main()
- {
- C c1(100,200,300,400,500);
- c1.Print();
- }
大家运行之后注意看下面的错误和警告,将会对大家的理解有重大作用哦。