C++ 派生类的构造和析构函数
派生类的目的是为了发展,派生类继承了积累的成员,实现了原有代码的重用,这只是一部分,而代码的扩充才是最主要的,只有通过添加新的成员,加入新的功能,类的派生才有实际意义。
派生类的构造函数只负责对新增的成员进行初始化,对所有从基类继承来的成员,其初始化工作还是由基类的构造函数完成。同样,对派生类对象的扫尾、清理工作,也需要加入新的的析构函数。
基类的构造函数并没有继承下来,要完成这些工作,就必须给派生类添加新的构造函数。派生类的构造函数需要以合适的初值作为参数,其中一些参数要用于对派生类新增成员进行初始化,另一些参数要分别传递给基类的构造函数和对象成员的构造函数,用于初始化相应的成员。
《什么时候需要声明派生类的构造函数?》
如果基类声明了带有形参表的构造函数时,派生类就应当声明构造函数,提供一个将参数传递给基类构造函数的途径,保证在基类进行初始化时能够获得必要的数据。当然,如果积累没有声明构造函数,派生类也可以不声明构造函数,全部采用默认构造函数,这时新增成员的初始化工作可以用其他公有函数来完成。
派生类构造函数执行的一般次序如下:
(1) 调用基类构造函数,调用顺序按照它们被继承时的顺序(从左至右)
(2) 调用内嵌成员对象的构造函数,调用顺序按照它们在类中声明的顺序
(3) 派生类构造函数体中的内容
其中,如果派生类中新增成员中有内嵌的对象,第二部调用才会执行,否则,就直接跳转到第三步。
示例:
#include<iostream>
usingnamespacestd;
class B1
{
public:
B1(inti){cout<<"Constructing B1 "<<i<<endl;} //基类B1,构造函数有参数
};
class B2
{
public:
B2(intj){cout<<"Constructing B2 "<<j<<endl;} //基类B2,构造函数有参数
};
class B3
{
public:
B3(){cout<<"Constructing B3 *"<<endl;} //基类B3,构造函数无参数
};
class C:public B2,public B1,public B3 //派生类C,注意此处的继承顺序!
{
public:
C(inta,intb, intc,intd):B1(a),memberB2(d),memberB1(c),B2(b){}
//注意基类名的个数与顺序
//注意成员对象名的个数与顺序
//memberB2和memberB1为内嵌对象
private:
B1memberB1;
B2memberB2;
B3memberB3;
};
intmain(){
Cobj(1,2,3,4);
system("pause");
return 0;
}
注意:
首先,这里并没有列出全部的基类和成员对象,由于B3类只有默认构造函数,不需要给它传递参数,
因此,基类B3以及B3类成员对象memberB3就不必列出。
其次,基类名和成员对象名的顺序是随意的。
这个派生类构造函数的函数体为空,可见实际上只是起到了传递参数和调用基类及内嵌对象构造函数的作用。
输出:
基类构造函数的调用顺序是按照派生类定义时的顺序,因此应该是先B2,再B1,再B3,而内嵌对象
的构造函数调用顺序应该是按照成员在类中声明的顺序,应该是先B1,再B2,再B3,程序运行的结果也完全印证这种分析。
《拷贝构造函数》
如果要为派生类编写拷贝构造函数,则需要为基类相应的拷贝构造函数传递参数。例如假设C类是B类的派生类,C类的拷贝构造函数形式如下:
C::C(C &c1):B(c1){…}
B类的拷贝构造函数参数类型应该是B类对象的引用,为什么要用C类对象的引用c1作为参数呢?
这是因为类型兼容规则在这里起到了作用:可以用派生类的引用去初始化基类的引用。因此当函数的形参是基类的引用时,实参可以是派生类的引用。
《析构函数》
执行次序和构造函数正好严格相反。
示例:
#include<iostream>
usingnamespacestd;
class B1
{
public:
B1(inti){cout<<"Constructing B1 "<<i<<endl;} //基类B1,构造函数有参数
~B1(){cout<<"Destructing B1"<<endl;system("pause");}
};
class B2
{
public:
B2(intj){cout<<"Constructing B2 "<<j<<endl;} //基类B2,构造函数有参数
~B2(){cout<<"Destructing B2"<<endl;system("pause");}
};
class B3
{
public:
B3(){cout<<"Constructing B3 *"<<endl;} //基类B3,构造函数无参数
~B3(){cout<<"Destructing B3"<<endl;system("pause");}
};
classC:publicB2,publicB1,publicB3 //派生类C,注意此处的继承顺序!
{
public:
C(inta,intb, intc,intd):B1(a),memberB2(d),memberB1(c),B2(b){}
//注意基类名的个数与顺序
//注意成员对象名的个数与顺序
private:
B1memberB1;
B2memberB2;
B3memberB3;
};
intmain(){
Cobj(1,2,3,4);
system("pause");
return 0;
}
运行结果: