实验二 继承与派生
一、实验目的
- 理解继承的概念、特性及在 C++语言中的实现方法;
- 掌握 C++语言派生类的声明与定义方法;
- 熟悉不同的继承方式(派生方式)的访问特性;
- 掌握派生类构造函数的定义以及在定义、释放派生类对象时构造函数、析构函数的执行顺序;
- 学习虚基类在解决二义性问题中的作用
二、实验内容
㈠ 验证及认知实验
按要求调试下列程序,并回答相关问题。
程序 1:
#include<iostream>
using namespace std;
class Base //基类Base的定义
{ private:
float x,y;
public:
Base(float a=0,float b=0)
{x=a;y=b;}
void print(void)
{ cout<<"x="<<x<<" y="<<y;}
};
class Derived:public Base //派生类Derived的定义(注意定义方法 )
{ private:
float z;
public:
Derived(float a=0,float b=0,float c=0):Base(a,b)
{ z=c;} //派生类的构造函数定义
void print(void) //派生类的成员函数
{ Base::print();//调用基类 Base 的成员函数 print();
cout<<" z="<<z;
}
};
int main(void)
{ Base a(10.0,20.0);
Derived b(10.0,20.0,30.0);
a.print();
cout<<endl;
b.print();
cout<<endl;
b.Base::print();
cout<<endl;
return 0;
}
问题:
- 运行程序的输出结果为:
x=10 y=20
x=10 y=20 z=30
x=10 y=20
- 程序中“a.print( );”调用的是____基类Base_______类成员中的“print()”成员函数,“b.print( );”调用的是____ 继承类Derived ___类成员中的“print()”成员函数,“b.Base::print();”调用的是____基类Base ___类成员中的“print()”成员函数。
- 构造函数“Derived(float a=0,float b=0,float c=0):Base(a,b)”中的“Base(a,b)”的作用 是:_____调用基类Base的构造函数____________。
- 将派生类定义中的“public”改为“private”,重新编译程序,程序中__b.Base::print();____语句会出现编译错,其原因是__“Base”是“Derived”的不可访问的基类__。
(二)知识应用实验
分析下列程序,先写出程序的输出结果,再上机运行程序验证其正确性,如果不正确,请认真分析出错原因。
(1)程序2:
#include<iostream>
using namespace std;
class Base
{ private:
float x,y;
public:
Base(float a=0,float b=0)
{x=a;y=b;
cout<<"基类构造函数被调用!\n";}
~Base()
{cout<<"基类析构函数被调用!\n";}
void print(void)
{ cout<<"x="<<x<<" y="<<y;}
};
class Derived:public Base
{ private:
float z;
public:
Derived(float a=0,float b=0,float c=0):Base(a,b)
{ z=c;
cout<<"派生类构造函数被调用!\n"; }
~Derived()
{cout<<"派生类析构函数被调用!\n";}
void print(void)
{ Base::print();
cout<<" z="<<z;
}
};
int main(void)
{ Base a(10.0,20.0);
Derived b(10.0,20.0,30.0);
a.print();
cout<<endl;
b.print();
cout<<endl;
}
你分析的程序运行结果是:
基类构造函数被调用!
派生类构造函数被调用!
基类构造函数被调用!
基类构造函数被调用!
X=10 y=10
X=10 y=10 z=30
派生类析构函数被调用!
基类析构函数被调用!
程序的实际输出结果是:
基类构造函数被调用!
基类构造函数被调用!
派生类构造函数被调用!
x=10 y=20
x=10 y=20 z=30
派生类析构函数被调用!
基类析构函数被调用!
基类析构函数被调用!
请解释原因:
派生类构造函数中的某些初始化可能是基于基类的,所以规定构造在类层次的最根处开始。而在每一层,首先调用基类构造函数,然后调用成员对象构造函数。
因为C++的成员变量是不会自动初始化的。
而析构相反:因为先析构外面的派生类才能析构里面的基类?
总结:如图,从内往外构造,从外向内析构。
(2)程序3:
#include<iostream>
using namespace std;
class Data
{ private:
int da;
public:
Data(int x=0)
{ da=x;
cout<<" Data 类的构造函数被调用!"<<endl; }
~Data()
{ cout<<" Data 类的析构函数被调用!"<<endl;}
};
class A
{ private :
int a;
public :
A(int x=0)
{ a=x;
cout<<" A 类的构造函数被调用!"<<endl; }
~A()
{ cout<<" A 类的析构函数被调用!"<<endl;}
};
class B : public A
{ private :
int b;
Data c;
public :
B(int x=0,int y=0,int z=0):A(x),c(y)
{ b=z;
cout<<" B 类的构造函数被调用!"<<endl;
}
~B()
{ cout<<" B 类的析构函数被调用!"<<endl;}
};
int main()
{ B a;}
你分析的程序输出结果是:
A类的构造函数被调用!
Data 类的构造函数被调用!
B 类的构造函数被调用!
B 类的析构函数被调用!
Data 类的析构函数被调用!
A 类的析构函数被调用!
程序的实际输出结果是:
A类的构造函数被调用!
Data 类的构造函数被调用!
B 类的构造函数被调用!
B 类的析构函数被调用!
Data 类的析构函数被调用!
A 类的析构函数被调用!
请解释原因:
1、构造函数的调用顺序
基类构造函数、对象成员构造函数、派生类本身的构造函数
2、析构函数的调用顺序
派生类本身的析构函数、对象成员析构函数、基类析构函数(与构造顺序正好相反)
(3)完善、调试通过下列程序,并按所要求回答问题。
//程序4
#include<iostream>
using namespace std;
class Base //基类 Base 的定义
{ private:
float x,y;
public:
Base(float a=0,float b=0)
{x=a;y=b;}
void setBase(float a=0,float b=0)
{x=a;y=b;}
void print(void)
{ cout<<"x="<<x<<"\ny="<<y<<endl;}
};
class Derived:public Base //派生类 Derived 的定义
{ private:
float z;
public:
Derived( (1) ): (2)
{ z=c; } //派生类的构造函数定义
void setDerived( (3) )
{ (4) ;z=c;}
void print(void) //派生类的成员函数
{ Base::print();//调用基类 Base 的成员函数 print();
cout<<"z="<<z<<endl;
}
};
int main(void)
{ Derived b(10.0,20.0,30.0);
b.print();
b.setDerived(15.5,25.5,35.5);
b.print();
}
程序中:(1)处应改为 float a=0,float b=0,float c=0
(2)处应改为 Base(a,b)
(3)处应改为 float a=0,float b=0,float c=0
(4)处应改为 Base::setBase(a,b)
(三)程序设计实验
1、已有类Time和Date,要求设计一个派生类Birthtime,它继承Time和Date,并且增加一个数据成员Childname 用于表示小孩的名字,同时设计主程序显示一个小孩的出生时间和名字。
class Time
{public:
Time (int h, int m,int s)
{
hours=h;
minutes=m;
seconds=s;
}
void display()
{
cout<<"出生日期"<<hours<<"时"<<minutes<<"分"<<seconds<<"秒"<<endl;
}
protected:
int hours,minutes,seconds;
};
{public:
Date (int m, int d,int y)
{
month=m;
day=d;
year=y;
}
void display()
{
cout<<"出生年月"<<year<<"年"<<month<<"月"<<day<<"日"<<endl;
}
protected:
int month,day,year;
};
(1) 类之间的继承关系图
(2)写出类的设计思路
设计一个派生类Birthtime,它继承Time和Date,并且增加一个(private) Childname数据成员用于表示小孩的名字,然后建立一个(public) display()函数进行名字的输出。
(3)完整代码
#include<iostream>
using namespace std;
class Time
{public:
Time (int h,int m,int s)
{
hours=h;
minutes=m;
seconds=s;
}
void display()
{
cout<<"出生日期"<<hours<<"时"<<minutes<<"分"<<seconds<<"秒"<<endl;
}
protected:
int hours,minutes,seconds;
};
class Date
{public:
Date (int m, int d,int y)
{
month=m;
day=d;
year=y;
}
void display()
{
cout<<"出生年月"<<year<<"年"<<month<<"月"<<day<<"日"<<endl;
}
protected:
int month,day,year;
};
class Birthtime:public Date,public Time
{
private:
string Childname;
public:
Birthtime(int a,int b,int c,int d,int e,int f,string g):Date(a,b,c),Time(d,e,f)
{
Childname=g;
}
void display()
{
cout<<"名字"<<Childname<<endl;
}
};
int main(){
Birthtime baby(06,03,2002,12,01,00,"小q");
baby.display();
baby.Date::display();
baby.Time::display();
}
(4)运行结果
2、设计职员类,它继承了Person类并组合了Date类。编写主程序显示职员数据如下:
姓名:杨萍
出生日期: 1988.10.6
性别:女
工作部门:团委
职务:团委书记
工资:6000
要求:画出类之间的继承关系图;写出类的设计思路;完整代码。
(1) 类之间的继承关系图
(2)写出类的设计思路
设计职员类,它继承了Person类并组合了Date类:
Person类中有数据成员姓名、性别,以及它们的显示函数,有参构造函数。
Date类中有数据成员年、月、日,显示函数,有参构造函数。
Employee类继承了Person类并组合了Date类(含有一个Date类对象数据成员),并增加了数据成员工作部门、职务、工资,显示函数,有参构造函数。
(3)完整代码
#include <iostream>
using namespace std;
class Person//继承Person类
{
private:
string name;//姓名
string sex;//性别
public:
Person(string n,string s){
name=n;
sex=s;
}
void getname(){
cout<<"姓名:"<<name<<endl;
}
void getsex(){
cout<<"性别:"<<sex<<endl;
}
};
class Date//组合Date类
{public:
Date (int y, int m,int d)
{
year=y;
month=m;
day=d;
}
void getdate()
{
cout<<"出生日期: "<<year<<"."<<month<<"."<<day<<endl;
}
protected:
int month,day,year;
};
class Employee:public Person{
private:
string department;//部门
string office;//职位
float salary;//工资
public:
Date date;//日期
Employee(string n,string s,int y, int m,int d,string de,string o,float sa):Person(n,s),date(y,m,d)
{
department=de;
office=o;
salary=sa;
}
void getemployee(){
cout<<"工作部门:"<<department<<endl;
cout<<"职务:"<<office<<endl;
cout<<"工资:"<<salary<<endl;
}
};
int main()
{
Employee em("杨萍","女",1988,10,6,"团委","团委书记",6000);
em.getname();
em.date.getdate();
em.getsex();
em.getemployee();
return 0;
}
(4)运行结果
三、实验收获与创新
收获:
1.弄清楚了继承类本身、对象成员、基类的构造函数、析构函数调用顺序
2.明白了继承和组合的关系
3.知道了当继承不同类的成员函数同名时如何进行调用
4.熟悉不同的继承方式(派生方式)的访问特性
创新:
1.通过调试试验发现多继承类调用基类的构造函数、析构函数只与声明顺序有关,与其他无关
2...