zoukankan      html  css  js  c++  java
  • 多重继承,虚继承,MI继承中虚继承中构造函数的调用情况

    先来测试一些普通的多重继承。其实这个是显而易见的。

    测试代码:

    [cpp] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. //测试多重继承中派生类的构造函数的调用顺序何时调用    
    2. //Fedora20 gcc version=4.8.2    
    3. #include <iostream>    
    4. using namespace std;    
    5.     
    6. class base    
    7. {    
    8. public:    
    9.     base()    
    10.     {    
    11.             
    12.         cout<<"base created!"<<endl;    
    13.     }    
    14.     ~base()    
    15.     {    
    16.         cout<<"base destroyed!"<<endl;    
    17.     }    
    18. };    
    19. //公有继承    
    20. class A:public base    
    21. {    
    22. public:    
    23.     A()    
    24.     {    
    25.         cout<<"A created!"<<endl;    
    26.     }    
    27.     ~A()    
    28.     {    
    29.         cout<<"A destroyed!"<<endl;    
    30.     }    
    31. };    
    32. class B:public base    
    33. {    
    34. public:    
    35.     B()    
    36.     {    
    37.         cout<<"B created!"<<endl;    
    38.     }    
    39.     ~B()    
    40.     {    
    41.         cout<<"B destroyed!"<<endl;    
    42.     }    
    43. };    
    44. class C:public B,public A//多重继承  
    45. //改为class C:public A,public B后,AB构造的顺序改变  
    46. {  
    47. public:  
    48.     C(){  
    49.         cout<<"C created!"<<endl;  
    50.     }  
    51.     ~C(){  
    52.         cout<<"C destroyed"<<endl;  
    53.     }  
    54.   
    55.   
    56. };  
    57. int main()  
    58. {  
    59.   
    60.     C test;  
    61.   
    62. }  

    测试结果:

    显而易见,普通的多重基层依次调用上一层父类的构造函数。这就说明了MI有时候不太好的情况了,那就是如果你不小心的话,就会从上一层继承关系中意外创建多个基类的对象,例如如果base里面有一个string name属性,那么D创建的时候就会创建两个string name的对象,而这个,不一定是你想要的结果。

    现在,来测试一下父类中存在虚继承的情况。

    测试代码:

    [cpp] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. //测试多重继承中派生类的构造函数的调用顺序何时调用    
    2. //Fedora20 gcc version=4.8.2    
    3. #include <iostream>    
    4. using namespace std;    
    5.     
    6. class base    
    7. {    
    8. public:    
    9.     base(){    
    10.         cout<<"base created!"<<endl;    
    11.     }    
    12.     ~base(){    
    13.         cout<<"base destroyed!"<<endl;    
    14.     }    
    15. };    
    16. //虚继承    
    17. class A:public virtual base    
    18. {    
    19. public:    
    20.     A(){    
    21.         cout<<"A created!"<<endl;    
    22.     }    
    23.     ~A() {    
    24.         cout<<"A destroyed!"<<endl;    
    25.     }    
    26. };    
    27. //虚继承  
    28. class B:public virtual base    
    29. {    
    30. public:    
    31.     B(){    
    32.         cout<<"B created!"<<endl;    
    33.     }    
    34.     ~B(){    
    35.         cout<<"B destroyed!"<<endl;    
    36.     }    
    37. };    
    38. //C是虚继承base  
    39. class C:public virtual base  
    40. {  
    41. public:  
    42.     C(){  
    43.         cout<<"C created!"<<endl;  
    44.     }  
    45.     ~C(){  
    46.         cout<<"C destroyed"<<endl;  
    47.     }  
    48. };  
    49. class D:public A,public B,public C  
    50. {//D A,B,C都是虚继承的形式。  
    51. public:  
    52.     D(){  
    53.         cout<<"D created!"<<endl;  
    54.     }  
    55.     ~D(){  
    56.         cout<<"D destroyed!"<<endl;  
    57.     }  
    58.   
    59. };  
    60. int main()  
    61. {  
    62.   
    63.     D d;  
    64.   
    65. }  


    测试结果:

    可以看到,如果上一层继承中都是虚继承,那么,只会在最开始一次调用base基类构造函数。

    那么,如果A不是虚继承呢?

    [cpp] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. //A不是虚继承    
    2. class A:public base    
    3. {    
    4. public:    
    5.     A(){    
    6.         cout<<"A created!"<<endl;    
    7.     }    
    8.     ~A() {    
    9.         cout<<"A destroyed!"<<endl;    
    10.     }    
    11. };    


    测试结果:

    看到虚继承的B,C依旧只是在最开始使调用了一次base,但是A类不再是虚继承,因此A类的构造函数也调用来一次base的构造函数.

    [admin@localhost csuper]$ ./c
    base created!      //这是BC共同调用的base构造函数
    base created!     //这是调用A类的构造函数时,A类构造函数又调用了一次base的构造函数。
    A created!

    为了测试这一想法是否真是如此,这里我们利用控制变量法,仅使B类不是虚继承。

    [cpp] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. //仅令B不是虚继承,A,C依旧是虚继承  
    2. class B:public  base    
    3. {    
    4. public:    
    5.     B(){    
    6.         cout<<"B created!"<<endl;    
    7.     }    
    8.     ~B(){    
    9.         cout<<"B destroyed!"<<endl;    
    10.     }    
    11. };    

    结果:

    可以看出,结果正是如此。

    同时发现了一个有趣的情况。当A,B,C都是虚继承base的时候,D虚继承C,看看结果又会如何?

    [cpp] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. class D:public A,public B,virtual public C  
    2. {// A,B,C都是虚继承base的形式。  
    [cpp] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. //D又虚继承C  
    2. public:  
    3.     D(){  
    4.         cout<<"D created!"<<endl;  
    5.     }  
    6.     ~D(){  
    7.         cout<<"D destroyed!"<<endl;  
    8.     }  
    9.   
    10. };  


    结果:

    可以看到,构造D的对象时,先调用了base,然后就到了调用C的构造函数了,说明编译器在构造的时候,是优先构造虚继承的对象的,这样就保证了构造A,B对象的时候,如果AB是虚继承于base,就不会创建多个从base定义的成员属性了。

    但是如果C不是虚继承base,但D又是虚继承C的时候又会如何呢?

    [cpp] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. //C不是虚继承base  
    2. class C:public base  
    3. {  
    4. public:  
    5.     C(){  
    6.         cout<<"C created!"<<endl;  
    7.     }  
    8.     ~C(){  
    9.         cout<<"C destroyed"<<endl;  
    10.     }  
    11. };  
    12. class D:public A,public B,virtual public C  
    13. {//D A,B,C都是虚继承的形式。  
    14. public:  
    15.     D(){  
    16.         cout<<"D created!"<<endl;  
    17.     }  
    18.     ~D(){  
    19.         cout<<"D destroyed!"<<endl;  
    20.     }  
    21.   
    22. };  

    结果:



    可以看出,第一个调用的base应该是属于A,B调用的,因此,其实上面的说法是不对的,因为如果是优先调用C的构造函数,输出应该是

    base created!   //如果是优先调用C
    C created!

    base created!   //A,B调用的base应该在这里出现,但事实上却不是。
    A created!
    B created!
    D created!
    ...........

    再看看下面的测试:

    [cpp] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. //测试多重继承中派生类的构造函数的调用顺序何时调用    
    2. //Fedora20 gcc version=4.8.2    
    3. #include <iostream>    
    4. using namespace std;    
    5.     
    6. class base    
    7. {    
    8. public:    
    9.     base(){    
    10.         cout<<"base created!"<<endl;    
    11.     }    
    12.     ~base(){    
    13.         cout<<"base destroyed!"<<endl;    
    14.     }    
    15. };    
    16. //A是虚继承    
    17. class A:public virtual base    
    18. {    
    19. public:    
    20.     A(){    
    21.         cout<<"A created!"<<endl;    
    22.     }    
    23.     ~A() {    
    24.         cout<<"A destroyed!"<<endl;    
    25.     }    
    26. };    
    27. //B不是虚继承  
    28. class B:public  base    
    29. {    
    30. public:    
    31.     B(){    
    32.         cout<<"B created!"<<endl;    
    33.     }    
    34.     ~B(){    
    35.         cout<<"B destroyed!"<<endl;    
    36.     }    
    37. };    
    38. //C不是虚继承base  
    39. class C:public base  
    40. {  
    41. public:  
    42.     C(){  
    43.         cout<<"C created!"<<endl;  
    44.     }  
    45.     ~C(){  
    46.         cout<<"C destroyed"<<endl;  
    47.     }  
    48. };  
    49. class D:public A,public virtual B,virtual public C  
    50. {// B,C都是虚继承的形式。  
    51. public:  
    52.     D(){  
    53.         cout<<"D created!"<<endl;  
    54.     }  
    55.     ~D(){  
    56.         cout<<"D destroyed!"<<endl;  
    57.     }  
    58.   
    59. };  
    60. int main()  
    61. {  
    62.   
    63.     D d;  
    64.   
    65. }  

    这里只有A是虚继承base,B,C都不是虚继承,但是在D里面,刚好相反,看看结果如何

    说实在的,输出是这样我是没有想到的。

    base created!   //这个base是哪一个调用的呢?
    base created!
    B created!
    base created!
    C created!

    后来我再这样测试一下我就知道是为什么了。

    令A,B,C均不虚继承与base,但是D继承与A,并且虚继承B,C,看结果

    因此可以看出,上一次的第一个base是因为A而调用的base,也就是说,编译器是先检测上一层继承关系(A,B,C)中,哪一个是虚继承于再上一层的类(base),如果有,则优先调用该虚继承与base的类(也就是A)的基类的(也就是base)构造函数,然后再调用D中虚继承的类的构造函数(B,C),最后才调用A自己的构造函数,这里有点复杂,还是上一次测试的例子:

    base created!   //这个base是因为A是虚继承与base时调用的,但是调用完之后并不直接调用A (A created!)
    base created!   //这个是调用B时调用的base
    B created!
    base created!   //这个是调用C时i调用的base
    C created!

    A created!    //最后才调用A created!

    因此,可以发现,如果你不想该类(例如A)在被继承的时候防止派生类(D)多次创建该类(A)的基类(base)的多个对象,那么,就应该将该类声明为virtual 继承基类(base),vitual的作用是对下一层次的派生类起作用,对该类(A)并不起特别作用(但是该类(D)会优先调用虚继承类(B,C)的构造函数)。

  • 相关阅读:
    康师傅JVM:运行时数据区概述及线程(三)
    康师傅JVM:程序计数器(四)
    Git常用命令
    Arthas概述
    康师傅JVM:JVM与Java体系结构(一)
    LabVIEW 连接MySQL数据库
    LabVIEW dll 崩溃
    LabVIEW 关于定时的研究
    NI 配置管理软件MAX的一些功能使用介绍
    LabVIEW 串口通信
  • 原文地址:https://www.cnblogs.com/zsq1993/p/5931234.html
Copyright © 2011-2022 走看看