再上一篇:10.4 构造函数与对象成员
上一篇:第十一章 继承和派生类
主页
下一篇:11.3 冲突、支配规则和赋值兼容性
再下一篇:11.4 虚基类
文章列表

11.2 初始化基类成员

《VC++程序设计基础》,讲述C++语言的语法和标准库,以及Visual C++ 函数库和MFC类库的使用,并附相关代码示例。

在基类中定义了基类的构造函数,并且在派生类中也定义了派生类的构造函数,那么,在产生派生类的对象时,一方面系统要调用派生类的构造函数来初始化在派生类中新增加的数据成员,另一方面系统要调用基类的构造来初始化派生类中的基类成员。这种调用基类的构造函数是由派生类的构造函数来确定的。为了初始化基类成员,派生类的构造函数的一般格式为:

ClassName::ClassName(args):Base1(args1),Base2(args2),...,Basen(argsn)

{

... //初始化派生类中的其它数据成员

}

其中,ClassName是派生类的类名,Base1、Base2 、...、Basen是相应基类的类名(构造函数名);args是带有类型说明的形式参数表;而调用基类的构造函数中的参数表是实参表,实参表中的每一个参数可以是表达式,它们可以使用派生类构造函数中的形参,也可以使用其它的常量,实参只与派生类的构造函数中的参数名有关,而与派生类中参数的顺序无关。把冒号后列举的要调用基类构造函数的列表称为初始成员列表。当初始化成员列表中的某一个基类的构造函数的实参表为空(无参数)时,则该基类的构造函数的调用可从成员初始化列表中删除。

当说明派生类的对象时,系统首先调用各基类的构造函数,对基类成员进行初始化,然后执行派生类的构造函数。如果某一个基类仍是派生类,则这种调用基类的构造函数的过程递归进行下去。当撤消派生类的对象时,析构函数的调用顺序正好与构造函数的顺序相反。

例11.4 输出派生类中构造函数与析构函数的调用关系。

//Ex11_4.h

#include

class Base1 {

int x;

public:

Base1(int a)

{

x=a;

cout<<"调用基类1的构造函数!\n";

}

~Base1( )

{

cout<<"调用基类1的析构函数!\n";

}

};

class Base2 {

int y;

public:

Base2(int a)

{

y=a;

cout<<"调用基类2的构造函数!\n";

}

~Base2( )

{

cout<<"调用基类2的析构函数!\n";

}

};

class Derived:public Base1, public Base2{

int z;

public:

Derived(int a,int b):Base1(a),Base2(20)

{

z=b;

cout<<"调用派生类的构造函数!\n";

}

~Derived( )

{

cout<<"调用派生类的析构函数!\n";

}

};

//Ex11_4.cpp

void main(void)

{

Derived c(100,200);

}

执行程序后输出以下6行:

调用基类1的构造函数!

调用基类2的构造函数!

调用派生类的构造函数!

调用派生类的析构函数!

调用基类2的析构函数!

调用基类1的析构函数!

若派生类中包含对象成员,则在派生类的构造函数的成员初始化列表中不仅要列举要调用的基类的构造函数,而且要列举调用的对象成员的构造函数。

例11.5 派生类中包含对象成员。

//Ex11_4.cpp

#include “Ex11_4.h”

class Der:public Base1, public Base2{

int z;

Base1 b1,b2;

public:

Der(int a,int b):Base1(a),Base2(20),b1(200),b2(a+b)

{

z=b;

cout<<"调用派生类的构造函数!\n";

}

~Der( )

{

cout<<"调用派生类的析构函数!\n";

}

};

void main(void)

{

Der d(100,200);

}

执行程序后输出以下10行:

调用基类1的构造函数!

调用基类2的构造函数!

调用基类1的构造函数!

调用基类1的构造函数!

调用派生类的构造函数!

调用派生类的析构函数!

调用基类1的析构函数!

调用基类1的析构函数!

调用基类2的析构函数!

调用基类1的析构函数!

根据输出的结果可以清楚地看出,在建立类Der 的对象d时,先调用基类的构造函数,再调用对象成员的构造函数,最后执行派生类的构造函数。在有多个对象成员的情况下,调用这些对象成员的构造函数的顺序取决于它们在派生类中说明的顺序。

注意,在派生类的构造函数的成员初始化列表中,对对象成员的初始化必须使用对象名;而对基类成员的初始化,使用的是对应基类的构造函数名。

例11.6 定义描述学生和职工的类,并实现测试和简单的输入/输出。

分析:不论是学生还是职工,均有如下描述信息:姓名、性别、出生年月,这可以定义一个基类。然后由基类定义一个学生的派生类,增加描述信息:所在班级、学号、专业、英语成绩和数学成绩。再由基类定义一个职工的派生类,增加描述信息:部门、职务、工资。这里对描述学生和职工基本情况作了简化,目的是缩短程序的长度。程序为:

#include

#include

class Person { //描述学生和职工的基类

char *Name; //姓名

char Sex[4]; //性别

int Year; //出生年份

unsigned char Month; //出生月份

unsigned char Day; //出生日期

public:

Person(char *name, char *sex, int year,int month,int day)

{

Name= new char [strlen(name)+1];

strcpy(Name,name);

Year=year;

strcpy(Sex,sex);

Month= (unsigned char ) month;

Day = (unsigned char ) day;

}

Person( ) //缺省的构造函数

{

Name=NULL;

strcpy(Sex,"");

Year =0;

Month=0;

Day=0;

}

~Person() //析构函数

{ if (Name) delete [ ] Name;}

void SetName(char *);

void SetSex(char *);

void SetDate(int,int,int);

char *GetName(){return Name;} //获取名字

char *GetSex() {return (char *)Sex;} //获取性别

void GetDate(int *,int *,int *);

void ShowPerson();

};

void Person::SetName(char *name) //置名字

{

if (Name) delete [ ] Name;

Name=new char [strlen(name)+1];

strcpy(Name,name);

}

void Person::SetSex(char *sex) //置性别

{ strcpy(Sex,sex);}

void Person::SetDate(int year,int month,int day) //置出生日期

{

Year=year;

Month=(unsigned char)month;

Day=(unsigned char) day;

}

void Person::GetDate(int *year,int *month,int *day) //取出生日期{

*year=Year;

*month=Month;

*day = Day;

}

void Person::ShowPerson() //输出描述人的基本信息

{

cout<<"姓名:"<

int year,month,day;

GetDate(&year,&month,&day);

cout<<"出生年份:"<

class Student:public Person{ //定义学生的派生类

char *Class; //班级

long No; //学号

char *Speciality; //专业

int English; //英语成绩

int Math; //数学成绩

public:

Student(char *,char *,int,int,int,char *,long,char *,int,int);

~Student( )

{ if (Class ) delete [ ] Class;

if(Speciality) delete [ ] Speciality;

}

Student()

{

Class=NULL;

No=0;

Speciality=NULL;

English=0;

Math=0;

}

void SetClass(char *);

void SetNo(long);

void SetSpeciality(char *);

void setScore(int,int);

char *GetClass( ){return Class;} //取班级号

long GetNo(){return No;} //取学号

char *GetSpeciality(){return Speciality;} //取专业名称

void GetScore(int *x,int * y) //取二门课的成绩

{*x=English;*y=Math;}

void ShowStudent();

};

Student::Student(char *name,char *sex,int year,int month, //学生类的构造函数

int day,char *clas,long no,char *spe,int english,

int math):Person(name,sex,year,month,day)

{

Class=new char [strlen(clas)+1];

strcpy(Class,clas); //班级

No=no; //学号

Speciality = new char [strlen(spe)+1];

strcpy(Speciality,spe); //专业

English=english; //英语成绩

Math=math;

}

void Student::SetClass(char *cla) //置班级名称

{

if (Class) delete [ ] Class;

Class=new char [strlen(cla)+1];

strcpy(Class,cla); //班级名称

}

void Student::SetNo(long no) //置学号

{ No=no; }

void Student::SetSpeciality(char *spe) //置专业名称

{

if (Speciality) delete [ ] Speciality;

Speciality = new char [strlen(spe)+1];

strcpy(Speciality,spe); //专业名称

}

void Student::setScore(int eng,int math) //置成绩

{

English=eng;

Math=math;

}

void Student::ShowStudent() //输出学生的信息

{

ShowPerson();

cout<<"班级:"<

int e,m;

GetScore(&e,&m);

cout<<"专业:"<

class Emploee:public Person{ //派生职工类

char *Depart; //部门

char *Job; //职务

int Salarry; //工资

public:

Emploee(char *,char *,int,int,int,char *,char *,int);

Emploee()

{

Depart=NULL;

Job=NULL;

Salarry=0;

}

~Emploee()

{ if (Depart) delete [ ] Depart;

if (Job) delete [ ] Job;

}

void SetDepart(char *);

void SetJob(char *);

void SetSalarry(int s){Salarry=s;} //置工资

char *GetDepart(){return Depart;} //取部门

char * GetJob(){return Job;} //取职务

int GetSalarry(){return Salarry;} //取工资

void ShowEmploee();

};

Emploee:: Emploee(char *name,char *sex,int year,int month,

int day,char *depart,char *job,

int salarry):Person(name,sex,year,month,day){

Depart=new char [strlen(depart)+1]; //部门

strcpy(Depart,depart);

Job = new char [strlen(job)+1];

strcpy(Job,job);

Salarry=salarry;

}

void Emploee:: SetDepart(char *depart)

{

if (Depart ) delete [ ] Depart;

Depart=new char [strlen(depart)+1];

strcpy(Depart,depart);

}

void Emploee:: SetJob(char *job)

{

if (Job) delete [ ] Job;

Job = new char [strlen(job)+1];

strcpy(Job,job);

}

void Emploee:: ShowEmploee() //输出职工信息{

ShowPerson();

cout<<"部门:"<

cout<<"工资:"<

}

void main(void)

{

Person p1("李 明","男",1962,10,25);

p1.ShowPerson();

Person p2("","",0,2,28);

p2.ShowPerson();

p2.SetName("张 三");

p2.ShowPerson();

char *name=p2.GetName();

cout << "Name:"<

Student s1("王英明","男",1971,5,6,"981012",980003l,"计算机",90,95);s1.ShowStudent();

Emploee e1("李 四","男",1979,3,15,"计算机系","讲师",1000);e1.ShowEmploee();

{

Emploee e2[3];

int i,year,month,day,salarry;

char name[30],sex[4],depart[40],job[40];

for(i=0;i<2;i++){

cout <<"输入职工姓名:";

cin >>name;

cout <<"输入性别:";

cin>>sex;

cout<<"输入出生年、月、日:";

cin >>year >> month>> day;

cout << "输入部门:";

cin >> depart;

cout <<"输入职务:";

cin >>job;

cout <<"输入工资:";

cin >>salarry;

e2[i].SetName(name);

e2[i].SetSex(sex);

e2[i].SetDate(year,month,day);

e2[i].SetDepart(depart);

e2[i].SetJob(job);

e2[i].SetSalarry(salarry);

}

for(i=0;i<2;i++) e2[i].ShowEmploee();

}

}

这个程序虽然比较长,但程序结构仍然很简单,并且在程序中均作了注解说明,读者可自行分析并读懂程序。

从本例可以看出,对于编写一些比较小的或比较简单的程序时,用面向对象的程序设计方法,可能会觉得反而程序显得复杂和冗长。但在编写较为复杂的程序时,其优越性就会充分体现出来。

另外,从本例也可以看出,继承性可以重复使用已经编写好的程序代码和已设计好的数据结构,从而防止程序代码和数据结构的重复设计和编写。继承的另一个好处是能使得程序更容易理解和维护,这是因为描述一个抽象事物的有关程序代码及数据结构都集中在一个类中,而不是分散在整个程序中;加上类有很好的封装性,对一个属于类的私有成员数据的修改,不会影响其它任何类或其它对象。