构造函数的主要作用是对数据初始化。
在派生类中, 如果对派生类新增的成员进行初始化,就需要加入派生类的构造函数。与此同时,对所有从基类继承下来的成员的初始化工作,还是由基类的构造函数完成,但是基类的构造函数和析构函数不能被继承,因此必须在派生类的构造函数中对基类的构造函数所需要的参数进行设置
C++语言的语法中规定,编程人员可以在派生类的构造函数中显示调用基类的构造函数;如果没有显示调用,则由编译器调用基类中的默认无参构造函数。
同样,对撤销派生类对象时的扫尾、清理工作也需要加入新的析构函数来完成。
但是当基类含有带参数的构造函数时,派生类必须定义构造函数,以提供熊派生类构造函数中把参数传递给基类构造函数的途径。
在C++语言中,派生类构造函数的一般形式为
派生类构造函数名(总参数表列):基类构造函数名(参数表列)
{
派生类中新增数据成员初始化语句
}
有些读者看到派生类构造函数的定义形式时,可能会感到有些眼熟,它不是和前面介绍的初始化表的形式很类似吗?
回顾一下之前介绍过的构造函数初始化表的例子:
Box::Box(int h, int w, int len):height(h), width(w), length(len){}
实际上,本章节介绍的在派生类构造函数中对基类成员初始化,就是前面介绍的构造函数初始化表。也就是说,不仅可以利用初始化表对构造函数的数据成员初始化,而且可以利用初始化表调用派生类的基类构造函数,实现对基类数据成员的初始化。即在同一个构造函数的定义中同时实现这两种功能。
派生类构造函数、基类构造函数、析构函数的执行顺序
在建立一个对象时,执行构造函数的顺序如下:
1、先调用基类构造函数,如果派生类构造函数中没有显示调用基类构造函数,那么系统会自动的隐式调用基类的默认无参构造函数;
2、再执行派生类构造函数(即派生类构造函数的函数体)。
在派生类对象释放时,析构函数的执行顺序与构造函数的执行顺序相反,先执行派生类析构函数,再执行其基类析构函数。
注意:如果派生类的构造函数在类外定义,在类中对派生类构造函数作声明时,不能列出基类构造函数。只在定义派生类构造函数时,才将基类构造函数以参数表列将它列出。
含有子对象的派生类的构造函数和析构函数
前面介绍过的派生类,其数据成员都是基本数据类型(如int, char)或系统提供的类型(如string)。实际上,派生类的数据成员还可以是基类的对象,称为子对象,即对象中的对象。对一个派生类的对象来说,它可能会包含三类数据:
第一类:通过继承获得的基类的数据成员;
第二类:自己新增的基本类型的数据成员;
第三类:自己新增加的子对象数据成员;
这种情况下,派生类构造函数的初始化任务应该包含3部分:
1、对基类数据成员初始化。
2、对子对象数据成员初始化。
3、对派生类数据成员初始化。
这种情况下,派生类构造函数的一般形式为:
派生类构造函数名(总参数表列):基类构造函数名(参数表列),子对象名(参数表)….
{
派生类中新增数据成员初始化语句
}
在定义派生类对象时,构造函数的调用顺序如下:
1、调用基类构造函数,对基类数据成员初始化。
2、调用子对象构造函数,对子对象数据成员初始化、
3、再执行派生类构造函数本身,对派生类数据成员初始化。
派生类构造函数的总参数表列中,应当包括基类构造函数和子对象构造函数参数表列中的参数(当然别忘记派生类自己的基本数据类型的参数)。基类构造函数和子对象的书写次序可以是任意的。