目录
派生类的构造函数和析构函数
前面已提到,基类的构造函数和析构函数是不能被继承的,在声明派生类时,派生类并未没有把基类的构造函数继承过来,构造函数的主要作用是对数据成员初始化,那么对继承过来的基类成员和新增的成员初始化的工作都需要由派生类的构造函数承担,也就是说,在执行派生类的构造函数时,使派生类的数据成员和基类中的数据成员同时都被初始化。
1、简单的派生类的构造函数
我们说的 “简单” 是指一个基类直接派生出一个子类(只有一个基类,且只有一级派生),派生类的数据成员中不包含基类的对象(即子对象)。
简单派生类的构造函数一般形式:
派生类构造函数名 (总参数列表) : 基类构造函数名(参数列表)
{
派生类中新增数据成员初始化语句
}
注意:只在定义构造函数时列出调用基类构造函数的部分,声明时不需要,调用基类构造函数时,可以直接使用常量或全局变量。
举个简单的例子:
#include <iostream>
using namespace std;
class Person{
int age ;
string name ;
char sex ;
public:
Person(int a=0,string n="xu",char s='b'):age(a),name(n),sex(s){}
void show_1(){
cout<<"age= "<<age<<endl;
cout<<"name= "<<name<<endl;
cout<<"sex= "<<sex<<endl;
}
};
class Student:public Person{
int score ;
int ranking ;
public:
Student(int a=0,string n="mu",char s='g',int sc=100,int r=1):Person(a,n,s),score(sc),ranking(r){}
void show_2(){
cout<<"score= "<<score<<endl;
cout<<"ranking= "<<ranking<<endl;
}
void display(){
show_1();
show_2();
}
};
int main(){
Student s;
s.show_1();
s.show_2();
}
上述派生类构造函数中有五个参数,其中前三个是用来传递给基类的构造函数,后面两个(sc和 p)是用来对派生类所增加的数据成员初始化的。
需要注意的是,在定义派生类的构造函数的时候,我们可以看到派生类构造函数名后面括号内的参数表包括参数的类型和参数名(如 int a),而基类构造函数名后面括号内的参数表列只有参数名而不包括参数类型(如 a ,n,s)这是因为在这里不是定义基类构造函数,而是调用基类构造函数,因此这些参数是实参而不是形参。它们可以是常量、全局变量和派生类构造函数总参数表中的参数。另外调用基类构造函数时传的实参和定义基类构造函数时指定的参数是相匹配的。
在派生类类外定义构造函数和前面定义普通类的构造函数一样,
Student :: Student(int a=0,string n="mu",char s='g',int sc=100,int r=1):Person(a,n,s),score(sc),ranking(r){}
定义时传给基类构造函数一个常量,
Student(int s='g',int sc=100,int r=1):Person(18,"song",s),score(sc),ranking(r){}
由于是调用基类的构造函数,所以派生类总参数表中对基类构造函数的初始化顺序不作要求,如:
Student(int s=1,int sc=100,int r=1):Person(s,"song",'g'),score(sc),ranking(r){}
最后需要注意的是,在建立一个对象时,执行构造函数的顺序是:派生类构造函数先调用基类构造函数,再执行派生类构造函数本身(即派生类构造函数的函数体)。对于上个例子来说,就是先初始化 name、age和sex,然后再初始化 score 和 ranking 。在派生类对象释放时,先执行派生类析构函数,再执行基类析构函数,这个后面再说。
2、有子对象的派生类的构造函数
以前所介绍过的类,其数据成员都是标准类型(如 int ,char )或系统提供的类型(如 string ),实际上,我们在类中的数据成员还可以包含类对象。我们在声明一个类的数据成员时定义了另一个类的对象,这个对象就是该类的子对象,也就是该类对象的内嵌对象,即对象中的对象。如同上个例子:
#include <iostream>
using namespace std;
class Person{
int age ;
string name ;
char sex ;
public:
Person(int a=0,string n="xu",char s='b'):age(a),name(n),sex(s){}
void show_1(){
cout<<"age= "<<age<<endl;
cout<<"name= "<<name<<endl;
cout<<"sex= "<<sex<<endl;
}
};
class Student:public Person{
int score ;
int ranking ;
Person p; //定义子对象
public:
Student(int a=18,string n="mu",char s='g',int a1=19,string n1="song",char s1='b',int sc=100,int r=1):Person(a,n,s),p(a1,n1,s1),score(sc),ranking(r){}
void show_2(){
cout<<"score= "<<score<<endl;
cout<<"ranking= "<<ranking<<endl;
}
void display(){
cout<<"* * *"<<endl;
p.show_1();
}
};
int main(){
Student s;
s.show_1();
s.show_2();
s.display();
}
派生类构造函数的任务应包括三个部分:1)对基类数据成员的初始化;2)对子对象数据成员初始化;3)对派生类数据成员初始化。即派生类构造函数的一般形式:
派生类构造函数名(总参数表):基类构造函数名(参数列表),子对象名(参数列表),
{
派生类中新增数据数据成员初始化语句;
}
当然子对象和基类的初始化都是调用其他的构造函数,所以在派生类中对其进行初始化时,只需传进实参即可。另外基类构造函数和子对象的调用次序可以是任意的,编译系统是根据相同的参数名来确定传递关系的。但是一般先写基类构造函数,与调用顺序一致。