您的位置:首页 > 其它

8.2 派生类的构造函数与析构函数

2016-06-14 06:37 197 查看


定义形式

派生类的构造函数的定义形式为:
    派生类名::派生类名(参数总表):基类名1(参数名表1)

    《,基类名2(参数名表2),……,基类名n(参数名表n)》,《成员对象名1(成员对象参数名表1),……,成员对象名m(成员对象参数名表m)》

    {

        ……//派生类新增成员的初始化

    }; //所列出的成员对象名全部为新增成员对象的名字

注意:
声明派生类的构造函数时,冒号及冒号以后部分必须略去。
所谓不能继承构造和析构,并不是不能利用,而是把基类的构造函数作为派生类构造函数的一部分,或说调用基类的构造函数。
派生类构造函数的定义,与包含成员对象类的构造函数相类似。
冒号后的基类名、成员对象名的次序可以随意,这里的次序与调用次序无关。
定义派生类构造函数中,只要基类不是使用缺省构造函数都要显式给出基类名和参数表。如果基类没有定义构造函数,则派生类也可以不定义,全部采用系统给定的缺省构造函数。如果基类定义了带有形参表的构造函数时,派生类就应当定义构造函数。
析构函数的功能是作善后工作,析构函数无返回类型也没有参数,情况比较简单。派生类析构函数定义格式与非派生类无任何差异,只要在函数体内把派生类新增一般成员处理好就可以了,而对新增的成员对象和基类的善后工作,系统会自己调用成员对象和基类的析构函数来完成。


执行次序

派生类构造函数各部分的执行次序为:
进行基类成员初始化——按各基类在派生类定义中的先后顺序,依次调用它们的构造函数。
若派生类中包含对象成员,还要进行对象成员初始化——按新增成员对象在类定义中排列的先后顺序,依次调用它们的构造函数。
派生类的构造函数体中的操作。

说明:
构造在类层次的根处开始,在每一层,首先调用基类构造函数,然后调用对象成员的构造函数。
析构函数各部分执行次序与构造函数相反,首先对派生类新增一般成员析构,然后对新增对象成员析构,最后对基类成员析构。


应用举例

【例8.1】由在册人员类公有派生学生类。如要求派生类总是和基类保持一致,原有的成员和访问方式被保留,只能采用公有派生来实现。在私有派生时要保持接口不变,则要在派生类中重编接口,去调用基类接口成员函数。所以绝大多数场合总是用公有派生。(查看完整源代码

首先来看基类:
#include<iostream>
#include<string>
using namespace std;
enum Tsex{mid,man,woman};
struct course{
    string coursename;
    int grade;
};
class Person{
    string IdPerson; //身份证号,18位数字
    string Name; //姓名
    Tsex Sex; //性别
    int Birthday; //生日,格式1986年8月18日写作19860818
    string HomeAddress; //家庭地址
    public:
    Person(string, string,Tsex,int, string);
    Person();
    ~Person();
    void SetName(string);
    string GetName(){return Name;}
    void SetSex(Tsex sex){Sex=sex;}
    Tsex GetSex(){return Sex;}
    void SetId(string id){IdPerson=id;}
    string GetId(){return IdPerson;}
    void SetBirth(int birthday){Birthday=birthday;}
    int GetBirth(){return Birthday;}
    void SetHomeAdd(string );
    string GetHomeAdd(){return HomeAddress;}
    void PrintPersonInfo();
};

分析基类的构造函数和析构函数:
Person::Person(string id, string name,Tsex sex,int birthday, string homeadd){
    IdPerson=id;
    Name=name;
    Sex=sex;
    Birthday=birthday;
    HomeAddress=homeadd;
}//作为管理程序,这个构造函数并无必要,因为数据总是另外输入的。仅为说明语法存在。
Person::Person(){
    IdPerson="#";Name="#";Sex=mid;
    Birthday=0;HomeAddress="#";
}
Person::~Person(){} //string内部动态数组的释放,由string自带的析构函数完成

基类的其他成员函数
void Person::SetName(string name){
    Name=name; //拷入新姓名
}
void Person::SetHomeAdd(string homeadd){
    HomeAddress=homeadd;
}
void Person::PrintPersonInfo(){
    int i;
    cout<<"身份证号:"<<IdPerson<<'\n'<<"姓名:"<<Name<<'\n'<<"性别:";
    if(Sex==man)cout<<"男"<<'\n';
    else if(Sex==woman)cout<<"女"<<'\n';
         else cout<<" "<<'\n';
    cout<<"出生年月日:";
    i=Birthday;
    cout<<i/10000<<"年";
    i=i%10000;
    cout<<i/100<<"月"<<i%100<<"日"<<'\n'<<"家庭住址:"<<HomeAddress<<'\n';
}

注意派生的学生类的定义方式和构造函数:
class Student:public Person{ //定义派生的学生类
    string NoStudent; //学号
    course cs[30]; //30门课程与成绩
    public:
    Student(string id, string name,Tsex sex,int birthday, string homeadd, string nostud);
    //注意派生类构造函数声明方式
    Student();
    ~Student();
    int SetCourse(string,int);
    int GetCourse(string);
    void PrintStudentInfo();
}; //这里省略了一些成员函数,如赋学号、取学号等
Student::Student(string id, string name,Tsex sex,int birthday, string homeadd, string nostud)
:Person(id,name,sex,birthday,homeadd){ //注意Person参数表不用类型
    int i ;
    NoStudent=nostud;
    for(i=0;i<30;i++){ //课程与成绩清空,将来由键盘输入
        cs[i].coursename="#";
        cs[i].grade=0;
    }
}
Student::Student(){ //基类默认的无参数构造函数不必显式给出
    int i;
    NoStudent="#";
    for(i=0;i<30;i++){ //课程与成绩清空,将来由键盘输入
        cs[i].coursename="#";
        cs[i].grade=0;
    }
}
Student::~Student(){} //基类析构函数以及成员对象析构函数自动调用

最后是学生类的其他成员函数(作为学生类并不完整):
int Student::SetCourse(string coursename,int grade){ //设置课程
    int i;
    bool b=false; //标识新输入的课程,还是更新成绩
    for(i=0;i<30;i++){
        if(cs[i].coursename=="#"){
            //判断表是否进入未使用部分
            cs[i].coursename=coursename;
            cs[i].grade=grade;
            b=false;
            break;
         }
        else if(cs[i].coursename==coursename){
            //是否已有该课程记录
            cs[i].grade=grade;
            b=true;
            break;
        }
   }
   if(i==30) return 0; //成绩表满返回0
   if(b) return 1; //修改成绩返回1
   else return 2; //登记成绩返回2
} //如需添加删除课程函数,应按顺序表方式删除课程
int Student::GetCourse(string coursename){
   int i;
   for(i=0;i<30;i++) if(cs[i].coursename==coursename) return cs[i].grade;
   return -1;
}//找到返回成绩,未找到返回-1
void Student::PrintStudentInfo(){
   int i;
   cout<<"学号:"<<NoStudent<<'\n';
   PrintPersonInfo();
   for(i=0;i<30;i++)//打印各科成绩
      if(cs[i].coursename!="#")cout<<cs[i].coursename<<'\t'<<cs[i].grade<<'\n';
      else break;
   cout<<"--------完-------- "<<endl;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: