虚基类 (转)

来源:互联网 发布:联通云计算有编制吗 编辑:程序博客网 时间:2024/05/23 13:19
 

首先提出几个问题:

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.附上整个分析的程序代码,供大家使用

view plaincopy to clipboardprint?
  1. #include <iostream>   
  2. using namespace std;  
  3. class A  
  4. {  
  5. public:  
  6.     int x;  
  7.     A(int a = 0){x = a; cout<<"调用基类A的构造函数"<<endl;}  
  8. };  
  9. class B1:virtual public  A  
  10. {  
  11. public:  
  12.     int y1;  
  13.     B1(int a = 0, int b = 0):A(b)  
  14.     {     
  15.         y1 = a;  
  16.         cout<<"调用派生类B1的构造函数"<<endl;  
  17.     }  
  18.     void Print(void)  
  19.     {   cout<<"B1:x="<<x<<",y1="<<y1<<endl;}  
  20. };  
  21.   
  22. class B2:virtual public  A  
  23. {  
  24. public:  
  25.     int y2;  
  26.     B2(int a = 0, int b = 0):A(b)  
  27.     {     
  28.         y2 = a;   
  29.         cout<<"调用派生类B2的构造函数"<<endl;  
  30.     }  
  31.     void Print(void)  
  32.     {   cout<<"B2:x="<<x<<",y2="<<y2<<endl;}  
  33. };  
  34.   
  35. class C: public B1, public B2  
  36. {  
  37. public:  
  38.     int z;  
  39.     C(int a, int b, int d, int e, int m):B1(a,b),B2(d,e)  
  40.     {   
  41.         z = m;  
  42.         cout<<"调用派生类C的构造函数"<<endl;  
  43.     }  
  44.     void Print()  
  45.     {  
  46.         B1::Print();  
  47.         B2::Print();  
  48.         cout<<"z="<<z<<endl;  
  49.     }  
  50. };  
  51. void main()  
  52. {  
  53.     C c1(100,200,300,400,500);  
  54.     c1.Print();  
  55.     c1.x = 400;  
  56.     c1.Print();  
  57. }  


 

5.下面是一种没有采用虚基类的一种错误方法
view plaincopy to clipboardprint?
  1. #include <iostream>   
  2. using namespace std;  
  3. class A  
  4. {  
  5. public:  
  6.     int x;  
  7.     A(int a = 0){x = a; cout<<"调用基类A的构造函数"<<endl;}  
  8. };  
  9. class B1: public  A  
  10. {  
  11. public:  
  12.     int y1;  
  13.     B1(int a = 0, int b = 0):A(b)  
  14.     {     
  15.         y1 = a;  
  16.         cout<<"调用派生类B1的构造函数"<<endl;  
  17.     }  
  18.   
  19. };  
  20.   
  21. class B2: public  A  
  22. {  
  23. public:  
  24.     int y2;  
  25.     B2(int a = 0, int b = 0):A(b)  
  26.     {     
  27.         y2 = a;   
  28.         cout<<"调用派生类B2的构造函数"<<endl;  
  29.     }  
  30.   
  31. };  
  32.   
  33. class C: public B1, public B2  
  34. {  
  35. public:  
  36.     int z;  
  37.     C(int a, int b, int d, int e, int m):B1(a,b),B2(d,e)  
  38.     {   
  39.         z = m;  
  40.         cout<<"调用派生类C的构造函数"<<endl;  
  41.     }  
  42.     void Print()  
  43.     {  
  44.         cout<<"x="<<x<<endl;  
  45.         cout<<"y1="<<y1<<"y2="<<y2<<endl;  
  46.         cout<<"z="<<z<<endl;  
  47.     }  
  48. };  
  49. void main()  
  50. {  
  51.     C c1(100,200,300,400,500);  
  52.     c1.Print();  
  53.       
  54. }  

大家运行之后注意看下面的错误和警告,将会对大家的理解有重大作用哦。