实验六 继承与派生
一、实验目的
- 掌握派生类的定义方法和实现,能正确访问继承层次中的各种类成员。
- 掌握继承机制中派生类的构造函数和析构函数的执行顺序。
二、实验内容
-
请改写教材例题7-1的Rectangle类公有继承Point类。要求:(30分)
(1)为派生类增加构造函数、析构函数,及其它必要的函数。
(2)派生类中有能够打印矩形信息的函数,包括的信息有矩形点的位置坐标、矩形长宽值及面积。 -
设计一个基类:学生类(Student),采用公有继承的方式派生出一个研究生类(PostGraduate),要求:(20分)
(1)Student类中包含:学号、姓名、性别、专业。
(2)要求在PostGraduate类中增加导师(tutor)、津贴(allowance)、研究方向(researchArea)。
(3)两个类中都包含:display()函数,用于输出本类中的成员信息。
可以用如下代码测试所建立的类是否正确。
int main(){
Student s1(“20190001”, “Michael”, “Male”, “Computer Science”);
//参数分别为:学号,姓名,性别,专业
s1.display();
PostGraduate p1(……);//导师:“Liu”,津贴:“1000”,研究方向:“Deep learning”
p1.display();
return 0;
}
-
定义一个基类BaseClass,它有公有成员函数func1(),func2(),函数内打印出类名及函数名,有私有数据成员int i。从它派生出类DerivedClass,有公有成员函数func1(),函数内打印出类名及函数名,私有数据成员int j,在主函数中分别用BaseClass及Derivedclass类的指针去调用func1(),func2,观察运行结果。要求:(20分)
(1)注意派生类的构造函数须写正确;
(2)分析通过指针调用成员函数的运行结果。 -
定义一个Document类,有数据成员name,从Document派生出Book类,增加数据成员pageCount。观察基类与派生类的构造函数和析构函数的调用顺序。(20分)
-
组合与继承有什么共同点和差异?通过组合生成的类与被组合的类之间的逻辑关系是什么?继承呢?请分析总结。(5分)
三、实验步骤及结果
- 请改写教材例题7-1的Rectangle类公有继承Point类。要求:(30分)
(1)为派生类增加构造函数、析构函数,及其它必要的函数。
(2)派生类中有能够打印矩形信息的函数,包括的信息有矩形点的位置坐标、矩形长宽值及面积。
#include<iostream>
using namespace std;
class Point{
public:
Point(float x, float y){
this->x=x; this->y=y;
}
void move(float offX,float offY){
x+=offX; y+=offY;
}
float getX() const{return x;}
float getY() const{return y;}
private:
float x,y;
};
class Rectangle:public Point{
public:
Rectangle(float x,float y,float w,float h):Point(x,y){
this->w=w; this->h=h;
}
float getH() const{return h;}
float getW() const{return w;}
~Rectangle(){
cout<<"调用了Rectangle的析构函数。"<<endl;
}
void zuobiao(){
cout<<"坐标为:("<<this->getX()<<","<<this->getY()<<")"<<"\t";
cout<<"("<<this->getX()+w<<","<<this->getY()<<")"<<"\t";
cout<<"("<<this->getX()<<","<<this->getY()+h<<")"<<"\t";
cout<<"("<<this->getX()+w<<","<<this->getY()+h<<")"<<endl;
}
double area(){
return this->h*this->w;
}
private:
float h,w;
};
int main(){
Rectangle r(2,3,4.1,5.7);
r.zuobiao();
cout<<"其面积为:"<<r.area()<<endl;
return 0;
}
运行结果:
- 设计一个基类:学生类(Student),采用公有继承的方式派生出一个研究生类(PostGraduate),要求:(20分)
(1)Student类中包含:学号、姓名、性别、专业。
(2)要求在PostGraduate类中增加导师(tutor)、津贴(allowance)、研究方向(researchArea)。
(3)两个类中都包含:display()函数,用于输出本类中的成员信息。
可以用如下代码测试所建立的类是否正确。
int main(){
Student s1(“20190001”, “Michael”, “Male”, “Computer Science”);
//参数分别为:学号,姓名,性别,专业
s1.display();
PostGraduate p1(……);//导师:“Liu”,津贴:“1000”,研究方向:“Deep learning”
p1.display();
return 0;
}
代码:
#include<iostream>
using namespace std;
#include<string>
class Student{
private:
int score; string name; string sex; string major;
public:
Student(int score0,string name0,string sex0,string major0):score(score0),name(name0),sex(sex0),major(major0){}
void display(){
cout<<"学号为:"<<score<<"\t";
cout<<"姓名为:"<<name<<"\t";
cout<<"性别为:"<<sex<<"\t";
cout<<"专业为:"<<major<<endl;
}
};
class PostGraduate: public Student{
public:
PostGraduate(int score0,string name0,string sex0,string major0,string tutor0,double allowance0,string researchArea0):Student(score0,sex0,sex0,major0){
tutor = tutor0;
allowance = allowance0;
researchArea = researchArea0;
}
void display(){
cout<<"导师为:"<<tutor<<'\t';
cout<<"津贴为:"<<allowance<<'\t';
cout<<"研究方向为:"<<researchArea<<'\t'<<endl;
}
private:
string tutor; double allowance; string researchArea;
};
int main(){
Student s1(20190001, "Michael", "Male", "Computer Science");
s1.display();
cout<<endl;
PostGraduate p1(20190001, "Michael", "Male", "Computer Science","Liu",1000,"deep learning");
p1.display();
return 0;
}
运行结果:
- 定义一个基类BaseClass,它有公有成员函数func1(),func2(),函数内打印出类名及函数名,有私有数据成员int i。从它派生出类DerivedClass,有公有成员函数func1(),函数内打印出类名及函数名,私有数据成员int j,在主函数中分别用BaseClass及Derivedclass类的指针去调用func1(),func2,观察运行结果。要求:(20分)
(1)注意派生类的构造函数须写正确;
(2)分析通过指针调用成员函数的运行结果。
#include<iostream>
using namespace std;
class BaseClass{
private:
int i;
public:
BaseClass(int i0=0):i(i0){}
void func1(){
cout<<"类名为:BaseClass"<<endl;
cout<<"函数名为:func1()"<<endl;
}
void func2(){
cout<<"类名为:BaseClass"<<endl;
cout<<"函数名为:func2()"<<endl;
}
};
class Derivedclass:public BaseClass{
public:
Derivedclass(int i0=0,int j0=0):BaseClass(i0),j(j0){}
void func1(){
cout<<"类名为:Derivedclass"<<endl;
cout<<"函数名为:func1()"<<endl;
}
private:
int j;
};
int main(){
BaseClass b(2);
BaseClass* B = &b;
B->func1();
B->func2();
Derivedclass d;
Derivedclass *D = &d;
D->func1();
D->BaseClass::func1();
return 0;
}
运行结果:
- 定义一个Document类,有数据成员name,从Document派生出Book类,增加数据成员pageCount。观察基类与派生类的构造函数和析构函数的调用顺序。(20分)
#include<iostream>
using namespace std;
#include<string>
class Document{
private:
string name;
public:
Document(){cout<<"调用了Document的构造函数。"<<endl;}
~Document(){cout<<"调用了Document的析构函数。"<<endl;}
};
class Book:public Document{
private:
int pageCount;
public:
Book(){cout<<"调用了Book的构造函数。"<<endl;}
~Book(){cout<<"调用了Book的析构函数。"<<endl;}
};
void text01(){
Book b1;
}
int main(){
text01();
return 0;
}
运行结果:
- 组合与继承有什么共同点和差异?通过组合生成的类与被组合的类之间的逻辑关系是什么?继承呢?请分析总结。(5分)
- 共同点:
继承和组合的使用都可以减少重复代码,同时若类B是类A的内嵌对象,则B类具有A类的全部数据。 - 不同点:
组合是通过在其他类中定义对象来使用类中的方法和属性,不能访问父类的任何接口。 而继承则是从父类中得到方法和属性,即可以得到父类的全部接口,并加以调用。 - 通过组合生成的类和被组合的类之间的逻辑关系:
组合生成的类具有被组合类的全部内容,但是并不包括被组合类的全部接口,就是通过被组合的类的对象只能访问组合类的成员函数,但不能直接访问到被组合类的成员函数。 - 继承类与基类的逻辑关系:
即通过派生类的对象不但可以访问派生类的成员函数,也能访问基类的成员函数,派生类是可以完全继承基类的任何内容的包括全部接口。
四、实验小结(5分)
问题与解决办法:
-
编译出错 :[Error] ‘sqrt’ was not declared in this scope
解决办法:运用sqrt函数需要头文件#include <math.h>。
-
编译出错,提示“[Error] expected before return”
解决办法:语句结束时的英文下的分号“;”输成了中文下的分号。改正后,错误消失。
-
编译出错,提示“ [Error] expected ‘;’ before ‘)’ token”
解决办法:for循环里的分号“;”写成了逗号“,”。改正后,错误消失。