9.9 在派生类中使用构造函数和析构函数
由于派生类继承了其基类的成员,所以在建立派生类的实例对象时,必须调用基类的构造函数来初始化派生类对象的基类成员。派生类的构造函数既可以隐式调用基类的构造函数,也可以在派生类的构造函数中通过给基类提供初始化值(利用了前面所讲过的成员初始化值语法)显式地调用基类的构造函数。
派生类不继承基类的构造函数和赋值运算符,但是派生类的构造函数和赋值运算符能调用基类的构造函数和赋值运算符。
派生类的构造函数总是先调用其基类构造函数来初始化派生类中的基类成员。如果省略了派生类的构造函数,那么就由派生类的默认构造函数调用基类的默认构造函数。析构函数的调用顺序和调用构造函数的顺序相反,因此派生类的析构函数在基类析构函数之前调用。
软件工程视点 9.3
假设生成派生类对象,基类和派生类都包含其他类的对象,则在建立派生类的对象时,首先执行基类成员对象的构造函数,接着执行基类的构造函数,以后执行派生类的成员对象的构造函数,最后才执行派生类的构造函数。析构函数的调用次序与调用构造函教的次序相反。
软件工程视点 9.4
建立成员对象的顺序是对象在类定义中的声明顺序。成员初始化值的顺序不影响建立对象的顺序。
软件工程视点 9.5
对继承关系而言,基类构造函数的调用顺序是派生类定义中指定的继承顺序,派生类成员初始化值列表中指定的基类构造函数的顺序不影响对象的建立顺序。
图 9.7 中的程序演示了基类和派生类的构造函数及析构函数的调用顺序。程序的第1行到第35行是一个简单的 Point 类,包含一个构造函数、一个析构函数以及protected数据成员x和y。构造函数和析构函数打印了调用它们的 Point 对象的信息。
第36行到第72行是一个简单的 Circle 类,它是通过 public 继承从 Point 类派生出来的。类 circle 包含一个构造函数、一个析构函数以及Private数据成员radius。构造函数和析构函数打印了调用它们的 Circle 对象的信息。为初始化基类的数据成员x和y, Circle 的构造函数用成员初始化值语法和传递变量a和b的值调用Point类的构造函数。
第73行到最后是层次结构 Point/Circle 的驱动程序。程序首先在main函数内实例化了一个Point对象。由于该对象在进入其范围后又立即退出其范围,所以调用了Point的构造函数和析构函数。然后,程序实例化了类Circle的对象circle1。这个过程调用了类Point的构造函数,从而输出了类Circle的构造函数传递给它的值,随后再输出Circle构造函数所指定的输出内容。接着,程序实例化了类Circle的对象circle2。
这个过程同样需要调用类Point和Circle的构造函数。注意,类Point的构造函数在执行Circle构造函数之前执行。main 函数结束时,程序为对象circle1和circle2调用析构函数。因为调用析构函数的顺序和调用构造函数的顺序相反,所以先为对象circle2调用析构函数,调用顺序是调用完类Circle的析构函数后,再调用类Point的析构函数。为对象circle2调用完析构函数后,再以相同的顺序为对象cirele1调用析构函数。
1 // Fig. 9.7: point2.h
2 // Definition of class Point
3 #ifndef POINT2_H
4 #define POINT2_H
5
6 class Point {
7 public:
8 Point( int = O,int = 0 ); // default constructor
9 ~Point(); // destructor
l0 protected: // accessible by derived classes
11 int x, y; // x and y coordinates of Point
12 };
13
14 #endif
15 // Fig. 9.7: point2.cpp
16 // Member function definitions for class Point
17 #include <iostream.h>
18 #include "peint2.h"
19
20 // Constructor for class Point
21 Point::Point( int a, int b )
22 {
23 x = a;
24 y = b;
25
26 cout << "Point constructor:"
27 << '[' << x << ", "<< y << ']' << endl;
28 }
29
30 // Destructor for class Point
31 Point::~Point()
32 {
33 cout << "Point destructor: "
34 << '[' << x << ", "<< y << ']' << endl;
35 }
36 // Fig. 9.7: circle2.h
37 // Definition of class Circle
38 #ifndef CIRCLE2_H
39 #define CIRCLE2_H
40
41 #include "point2.h"
42
43 class Circle : public Point {
44 public:
45 // default constructor
46 Circle( double r = 0.0, int x = O, int y = 0 );
47
48 ~Circle();
49 private:
50 double radius;
51 };
52
53 #endif
54 // Fig. 9.7: circle2.cpp
55 // Member function definitions for class Circle
56 #include "circle2.h"
57
58 // Constructor for Circle calls constructor for Point
59 Circle::Circle( double r, int a, int b )
60 : Point( a, b ) // call base-class Constructor
61 {
62 radius = r; // should validate
63 cout << "Circle constructor: radius is"
64 << radius << "[" << x << ", "<< y << ']' << endl;
65 }
66
67 // Destructor roi class Circle
68 Circle::~Circle()
69 {
70 cout << "Circle destructor: radius is "
71 << radius << " [ " << x << ", "<< y << ']' << endl;
72 }
73 // Fig. 9.7: fig09_07.cpp
74 // Demonstrate when base-class and derived-class
75 // constructors and destructors are called.
76 #include <iostream.h>
77 #include "point2.h"
78 #include "circle2.h"
79
80 int main()
81 {
82 // Show constructor and destructor calls for Point
83 {
84 Point p( 11, 22 );
85 }
86
87 cout << endl;
88 Circle circle1( 4.5, 72, 29 );
89 cout << endl;
90 Circle circle2( 10, 5, 5 );
91 cout << endl;
92 return 0;
93 }
输出结果:
Point constructor: [ 11, 22 ]
Point destructor: [ 11, 22 ]
Point constructor: [ 72, 29 ]
Circle constructor: radius is 4.5 [ 72, 29]
Point constructor: [ 5, 5 ]
Circle constructor: radius is 10 [ 5, 5 ]
Circle destructor: radius is 10 [ 5, 5 ]
Point destructor: [ 5, 5 ]
Circle destructor: radius is 4.5 [ 72, 29 ]
Point destructor: [ 72, 29 ]
图 9.7 基类和派生类的构造函数和析构函数的调用顺序