派生类继承了基类的成员,实现了原有代码的重用,这仅仅是引入继承的目的之一。引入继承的更主要的目的是代码的扩充,只有在派生类中通过添加新的成员,加入新的功能,类的派生才更有意义。但是基类的构造函数和析构函数不能被继承,在派生类中,如果对派生类新增的成员进行初始化,就需要加入派生类的构造函数。与此同时,对所有从基类继承下来的成员的初始化工作,还是由基类的构造函数完成的,但是我们必须在派生类中对基类的构造函数所需要的参数进行设置。同样,对撤销派生类对象时的扫尾、清理工作也需要加入新的析构函数来完成。
1. 派生类构造函数和析构函数的执行顺序
通常情况下,当创建派生类的对象时,首先执行基类的构造函数,随后再执行派生类的构造函数;当撤销派生类对象时,则先执行派生类的析构函数,随后再执行基类的析构函数。
2. 派生类构造函数和析构函数的构造规则
2.1 简单的派生类的构造函数
当基类的构造函数没有参数,或没有显示定义构造函数时,派生类可以不向基类传递参数,甚至可以不定义构造函数。当基类含有带参数的构造函数时,派生类必须定义构造函数,以提供把参数传递给基类构造函数的途径。
在C++中,派生类构造函数的一般格式为:
1 | 派生类名(总参数表):基类名(参数表){ |
下面的程序说明如何传递一个参数给派生类的构造函数和传递一个参数给基类的构造函数。
1 |
|
说明:
(1)可以将派生类构造函数定义在类的外部,而在类体内只写该函数的声明。
1 | UStudent(int number1,string name1,float score1,string major1); |
而在类的外部定义派生类的构造函数:
1 | UStudent::UStudent(int number1,string name1,float score1,string major1) |
注意:在类中声明派生类构造函数时,不包括基类构造函数名及其参数表(即Student(number1,name1,score1)),只在类外定义构造函数时才将它列出。
(2)若在类使用默认构造函数或不带参数的构造函数,则在派生类中定义构造函数时可略去”:基类名(参数表)”,此时若派生类不需要构造函数,则可不定义派生类构造函数。
(3)当基类构造函数不带参数时,派生类不一定需要定义构造函数,然后当基类的构造函数哪怕只带有一个参数,它所有的派生类都必须定义构造函数,甚至所定义的派生类构造函数的函数体可能为空,仅仅起参数的传递作用。
2.2 派生类的析构函数
在派生类中可以根据需要定义自己的析构函数,用来对派生类中的所增加的成员进行清理工作。基类的清理工作仍然由基类的析构函数负责。由于析构函数是不带参数的,在派生系统中是否要自定义析构函数与它所属基类的析构函数无关。在执行派生类的析构函数时,系统会自动调用基类的析构函数,对基类的对象进行清理工作。
2.3 含有对象成员(子对象)的派生类的构造函数
当派生类中含有内嵌的对象成员(也称子对象)时,其构造函数的一般形式为:
1 | 派生类名(参数总表):基类名(参数表0),对象成员名1(参数表1),......,对象成员名n(参数表n){ |
在定义派生类对象时,构造函数的执行顺序如下:
- 调用基类的构造函数,对基类数据成员初始化;
- 调用内嵌对象成员的构造函数,对内嵌对象成员的数据成员初始化;
- 执行派生类的构造函数体,对派生类数据成员初始化。
撤销对象时,析构函数的调用顺序与构造函数的调用顺序正好相反。
1 |
|
说明:
(1)在派生类中含有多个内嵌对象成员时,调用内嵌对象成员的构造函数顺序由它们在类中声明的顺序确定。
(2)如果派生类的基类也是一个派生类,每个派生类只需要负责其直接基类数据成员的初始化。依次上溯。