分清继承关系中基类和子类构造函数调用顺序

开发 开发工具
这里将介绍继承关系中基类和子类构造函数调用顺序,希望本文能对大家有所帮助。

我们这里将分析一下继承关系中基类和子类构造函数调用顺序,希望通过本文,能使大家对于继承的理解更加深刻。

首先回顾并讨论先有鸡还是先有蛋的问题在C++中将会是什么情况。如果编写:

  1. class Egg;  
  2. class Hen  
  3. {  
  4. public:  
  5. int n;  
  6. Egg egg;  
  7.     Hen() {  
  8.         n=5;  
  9.         cout<<"Hen's con "<   
  10.     }  
  11. };  
  12. class Egg : public Hen  
  13. {  
  14. public:  
  15.     int m;  
  16.     Egg(){  
  17.         m=10;  
  18.         cout<<"Egg's con"<   
  19.     }  
  20. };  
  21. int main()  
  22. {  
  23.     Egg dan;  

这在C++中是无法编译通过的,首先,学过编译原理的都应该知道,所有语言在编译的时候都需要确定一个类的大小。C++的编译器在编译一个类的时候,需要分析这个类的大小,而sizeof(Egg)=sizeof(Hen)+sizeof(Egg)+…,编译器无法获知其大小,自然也无法编译通过;而JAVA、C#则不同,其类的成员皆为基本类型或引用。同时,和Java、C#等语言不同,C++不能做全局优化编译(即使打开全局优化开关也没有用),它的编译是逐步向后的分析方式。C++这样做,也在编译时就防止出现先有鸡还是先有蛋而产生的矛盾。大家可以尝试在C++中尝试其他方法看编译是否能够通过。

JAVA虽然可以在Hen类的初始化时对Egg进行构造(通过new Egg()),但运行时会出现堆栈溢出的错误:

  1. Exception in thread "main" java.lang.StackOverflowError  
  2.     at Egg.(…)  
  3.     at Hen.(…)  
  4.     at Egg.(…)  
  5.     at Hen.(…)  
  6. … 

下面回到本文的主题。我们知道,C++和JAVA不一样,C++子类是默认调用基类构造函数的,而JAVA则需要super()。为了研究基类和子类构造函数的调用顺序问题,以上述程序为基础,我编写了这样一个测试:

  1. class Hen  
  2. {  
  3. public:  
  4.     int n;  
  5.     Hen() {  
  6.         n=5;  
  7.         cout<<"Hen's con "<   
  8.     }  
  9.    Hen(int i) {  
  10.         n=i;  
  11.         cout<<"Hen's con "<   
  12.     }  
  13. };  
  14. class Hen1  
  15. {  
  16. public:  
  17.     int x;  
  18.     Hen1() {  
  19.        x=6;  
  20.         cout<<"Hen1's con "<   
  21.     }  
  22. };  
  23. class Hen2  
  24. {  
  25. public:  
  26.     int y;  
  27.     Hen2() {  
  28.         y=7;  
  29.        cout<<"Hen2's con "<   
  30.     }  
  31. };  
  32. class Egg : public Hen, public Hen2, public Hen1  
  33. {  
  34. public:  
  35.     int m;  
  36.     Hen hen;  
  37.     Hen1 hen1;  
  38.     Hen2 hen2;  
  39.     Egg(int i) : Hen2(),Hen1(),Hen(),hen(i), hen2(), hen1(){  
  40.         m=10;  
  41.         cout<<"Egg's con"<   
  42.     }  
  43. };  
  44. int main()  
  45. {  
  46.     Egg dan(1);  
  47. }  
  48. Output:  
  49. Hen's con 5  
  50. Hen2's con 7  
  51. Hen1's con 6  
  52. Hen's con 1  
  53. Hen1's con 6  
  54. Hen2's con 7  
  55. Egg's con 

通过这段程序可得如下结论:

1、基类构造函数。如果有多个基类,则构造函数的调用顺序是某类在类继承表中出现的顺序,而不是它们在成员初始化表中的顺序。如这里,是按照“class Egg : public Hen, public Hen2, public Hen1”的顺序

2、成员类对象构造函数。如果有多个成员类对象,则构造函数的调用顺序是对象在类中被声明的顺序,而不是它们出现在成员初始化表中的顺序。如这里,是按照Egg声明里

Hen hen;

Hen1 hen1;

Hen2 hen2;

的顺序。

总1、2而言之,成员初始化表顺序对于构造和赋值顺序没有任何意义

3、成员类对象并不是一开始就被构造,再根据初始化表赋值,而是在调用构造函数的时候,根据传入的参数根据成员初始化表中进行一次构造,构造顺序是对象在类中声明的顺序。

【编辑推荐】

  1. C#从C和C++继承的特点浅谈
  2. 实现C#继承与C#多态的实例演示
  3. 关于interface继承来源的讨论
  4. 学习C#接口继承
  5. C#继承构造函数实现及调用浅析
责任编辑:彭凡 来源: 51CTO.com
相关推荐

2013-03-04 11:10:03

JavaJVM

2010-01-20 18:06:06

C++虚基类

2009-08-13 18:36:36

C#继承构造函数

2009-12-10 13:37:16

PHP parent

2009-10-23 11:31:05

CLR Via C#调

2009-06-18 09:51:25

Java继承

2023-12-07 07:41:15

JavaScript函数原型

2009-08-13 18:26:35

C#继承构造函数

2011-08-24 13:56:27

JavaScript

2009-12-11 10:42:00

Scala讲座类定义构造函数

2009-12-16 09:43:35

Ruby父类Objec

2009-08-13 18:15:06

C#继承构造函数

2022-01-04 19:33:03

Java构造器调用

2020-06-17 12:22:44

C覆盖重载

2009-09-03 13:14:55

C#构造函数C#析构函数

2009-07-31 16:06:50

成员函数构造函数C#

2011-08-08 09:51:52

Cocoa 框架

2009-09-04 11:15:07

选择C#构造函数

2009-08-14 09:15:28

C#调用构造函数

2012-02-27 14:09:00

Java
点赞
收藏

51CTO技术栈公众号