5、继承与派生4-派生类的构造和析构函数
派生类的构造函数只负责对派生类新增的成员进行初始化,对所有从基类继承下来成员,其初始化工作还是由基类的构造函数完成。同样,对派生类对象的扫尾、清理工作也需要加入新的析构函数。
1、构造函数
构造派生类的对象时,就要对基类数据成员、新增数据成员和成员对象的数据成员进行初始化。派生类的构造函数需要以合适的初值作为参数,其中一些参数要用于对派生类新增的成员进行初始化,另一些参数要分别传递给基类的构造函数和对象成员的构造函数。用于初始化相应的成员。
在构造派生类的对象时,会首先隐含调用基类和内嵌对象成员的构造函数,来初始化他们各自的数据成员,然后才执行派生类构造函数的函数体。
派生类构造函数的一般语法形式为:
派生类名::派生类名(参数总表):基类名1(参数表1),...,基类名n(参数表n),
内嵌对象名1(内嵌对象参数表1),...,内嵌对象名m(内嵌对象参数表m)
{
派生类新增成员的初始化语句;
}
这里的派生类的构造函数名与类名相同。在生成派生类对象时,系统首先会使用这里列出的参数,调用基类和内嵌对象成员的构造函数。
下面来讨论什么时候需要声明派生类的构造函数。如果基类声明了带有形参表的构造函数时,派生类就应当声明构造函数,提供一个将参数传递给基类构造函数的途径,保证在基类进行初始化时能够获得必要的数据。
派生类构造函数执行的一般次序如下:
1)调用基类构造函数,调用顺序按照他们被继承时声明的顺序(从左向右)
2)调用内嵌成员对象的构造函数,调用顺序按照他们在类中声明的顺序
3)派生类的构造函数体中的内容。
其中,如果派生类中新增成员中有内嵌对象,第二步的调用才会执行。
eg:派生类构造函数举例(多继承,含有内嵌对象)
这是一个具有一般性特征的例子,有三个基类B1、B2和B3,其中B3只有一个默认的构造函数,其余两个基类的成员只有一个带有参数的构造函数。类c由这三个基类经过公有派生而来。派生类新增加了三个私有对象成员,分别是B1、B2和B3类的对象,如下
#include<iostream>
using namespace std;
class B1
{
public:
B1(int i){cout<<"constructing B1"<<i<<endl;}
};
class B2
{
public:
B2(int j){cout<<"constructing B2"<<j<<endl;}
};
class B3
{
public:
B3(){cout<<"constructing B3 *"<<endl;}
};
class C:public B2,public B1,public B3 //派生新类C,注意基类名的顺序
{
public:
C(int a,int b,int c,int d):B1(a),memberB2(d),memberB1(c),B2(b){}
//注意基类名的个数与顺序;成员对象名的个数与顺序
private:
B1 memberB1;
B2 memberB2;
B3 memeberB3;
};
int main()
{
C obj(1,2,3,4);
}
因为基类及内嵌对象成员都具有非默认形式的构造函数,所以派生类中需要声明一个非默认形式(即带参数)的构造函数。这个派生类构造函数的主要功能就是初始化基类及内嵌对象成员。需要注意的是:首先,这里并没有列出全部基类和成员对象,由于B3类只有默认构造函数,不需要给它传递参数,因此基类B3以及B3类成员对象memberB3就不必列出,但是系统还是会自动调用该类的默认构造函数。其次,基类名和成员对象名的顺序是随意的。这个派生类的构造函数的函数体为空,可见实际上只是起到了传递参数和调用基类及内嵌对象的构造函数的作用。
2、拷贝构造函数
若建立派生类对象时调用默认拷贝构造函数,则编译器将自动调用基类的拷贝构造函数。
如果要为派生类编写拷贝构造函数,则需要为基类相应的拷贝构造函数传递参数。如:C类是B类的派生类,C类的拷贝构造函数形式如下:
C::C(C &c1):B(c1)
{...}
这里B的拷贝构造函数的引用使用c1,这是由于类型兼容规则的原因,可以用派生类的引用去初始化基类的引用。
3、析构函数
派生类的析构函数的功能是在该类对象消亡之前进行一些必要的清理工作。它没有类型也没有参数。
派生类析构函数的声明方法与没有继承关系的类中析构函数的声明方法完全相同,只要在函数体中负责把派生类新增的非对象成员的清理工作做好就够了,系统会自己调用基类及对象成员的析构函数来对基类及对象成员进行清理。但他的执行次序和构造函数正好严格相反,首先对派生类新增普通成员进行清理,然后对派生类新增的对象成员进行清理;最后对所有从基类继承来的成员进行清理。这些清理工作分别是执行派生类析构函数体,调用派生类对象成员所在类的析构函数和调用基类析构函数。
eg:派生类析构函数举例(多继承、含有嵌入对象)
#include<iostream>
using namespace std;
class B1
{
public:
B1(int i){cout<<"constructing B1"<<i<<endl;}//B1的构造函数
~B1(){cout<<"destructing B1"<<endl;}//B1的析构函数
};
class B2
{
public:
B2(int j){cout<<"constructing B2"<<j<<endl;}//B2的构造函数
~B2(){cout<<"destructing B2"<<endl;}//B2的析构函数
};
class B3
{
public:
B3(){cout<<"constructing B3 *"<<endl;}//B3的构造函数
~B3(){cout<<"destructing B3"<<endl;}//B3的析构函数
};
class C:public B2,public B1,public B3 //派生类C定义
{
public:
C(int a,int b,int c,int d):B1(a),memberB2(d),memberB1(c),B2(b){}//派生类的构造函数定义
private:
B1 memeberB1;
B2 memberB2;
B3 memberB3;
};
int main()
{
C obj(1,2,3,4);
}
结果如下
程序中,我们给三个基类分别加入了析构函数,派生类没有做任何改动,仍然使用的是由系统提供的默认析构函数。主函数也保持原样。程序在执行时,首先执行派生类的构造函数,然后执行派生类的析构函数。派生类默认的析构函数有分别调用了成员对象及基类的析构函数。