实验五 继承与派生
实验目的和要求
1.掌握派生类的定义方法的和派生类构造函数定义方法。
2.掌握在不同继承方式的情况下,基类成员在派生类中的访问权限。
3.掌握在多继承方式的情况下,构造函数与析构函数的调用时机与顺序。
实验内容
1.调试下列程序,并在对程序进行修改后再调试,指出调试中出错原因。
- //sy5_1.cpp
- #include<iostream>
- using namespace std;
- class A
- {
- public:
- void seta(int i){a=i;}
- int geta(){return a;}
- public:
- int a;
- };
- class B:public A
- {
- public:
- void setb(int i){b=i;}
- int getb(){return b;}
- void show(){cout<<"A::a="<<a<<endl;}
- public:
- int b;
- };
- int main()
- {
- B bb;
- bb.seta(6);
- bb.setb(3);
- bb.show();
- cout<<"A::a="<<bb.a<<endl;
- cout<<"B::b="<<bb.b<<endl;
- cout<<"A::a="<<bb.geta()<<endl;
- cout<<"B::b="<<bb.getb()<<endl;
- return 0;
- }
按下列要求对程序进行修改,然后调试,对出现的错误分析其原因。
(1)将派生类B的继承方式改为private时,会出现哪些错误和不正常现象?为什么?
答:将派生类B的继承方式改为private时,编译程序指出语句2、语句5、语句7有错。这是因为当类的继承方式为私有继承时,基类A的公有成员函数a,公有成员函数seta()和geta()作为派生类B的私有成员,派生类成员可以直接访问它们,但在类外部,派生类的对象无法访问它们。
(2)将派生类B的继承方式改为protected时,会出现哪些错误和不正常现象?为什么?
答:将派生类B的继承方式改为protected时,编译程序指出语句2、语句5、语句7有错。这是因为当类的继承方式为保护继承时,基类A的公有数据成员a,公有成员函数seta()和geta()作为派生类B的保护成员,派生类成员可以直接访问它们,但在类外部,派生类的对象无法访问它们。
(3)将派生类B的继承方式恢复为public,再将类A中的数据成员int型变量a的访问权限改为private时,会出现哪些错误和不正常现象?为什么?
答:主函数中的语句5、语句9出现了错误;因为B采用的是公有继承,所以B中和主函数中不能访问A中的私有函数。
(4)派生类B的继承方式仍为public,将类A中的数据成员int型变量a的访问权限改为protected时,会出现哪些错误和不正常现象?为什么?
答:主函数中的语句5出现了错误;因为B采用公有继承,所以A中保护成员在B中仍为保护成员,B中可以访问A中的保护成员,但不能通过类的对象访问类的保护成员。
2.重写教材中的Li4_10.cpp,给每个类增加一个析构函数,并使类之间的关系如附图1所示,再写出程序的输出结果。(sy5_2.cpp)
附图1 类之间的关系
修改程序如下:
- //sy5_2.cpp
- #include<iostream>
- using namespace std;
- class Base1
- {
- public:
- Base1(){cout<<"constructing Base1"<<endl;}
- ~Base1(){cout<<"destructing Base1"<<endl;}
- };
- class Base2
- {
- public:
- Base2(){cout<<"constructing Base2"<<endl;}
- ~Base2(){cout<<"destructing Base2"<<endl;}
- };
- class Derived1:public Base2,virtual public Base1
- {
- public:
- Derived1(){cout<<"constructing Derived1"<<endl;}
- ~Derived1(){cout<<"destructing Derived1"<<endl;}
- };
- class Derived2:public Base2,virtual public Base1
- {
- public:
- Derived2(){cout<<"constructing Derived2"<<endl;}
- ~Derived2(){cout<<"destructing Derived2"<<endl;}
- };
- class Derived3:public Derived1,virtual public Derived2
- {
- public:
- Derived3(){cout<<"constructing Derived3"<<endl;}
- ~Derived3(){cout<<"destructing Derived3"<<endl;}
- };
- int main()
- {
- Derived3 obj;
- return 0;
- }
程序输出结果如下:
3. 利用继承性与派生类来管理学生和教师的档案。假设要管理下述几类人员的如下一些数据。
teacher(教师)类:姓名、性别、年龄、学号、系别;
gradstudent(研究生)类:姓名、性别、年龄、学号、系别、导师;
要求每个类只设立构造函数以及显示类对象数据的成员函数。编写主函数,说明有关类对象,并对其类成员函数进行简单使用。(sy5_3.cpp)
- #include<iostream>
- #include<string>
- using namespace std;
- class teacher{
- public:
- teacher(string name,char sex,int age,string title,string course)
- {
- nam=name;
- s=sex;
- a=age;
- t=title;
- c=course;
- }
- void print()
- {
- cout<<"name: "<<nam<<endl;
- cout<<"sex: "<<s<<endl;
- cout<<"age: "<<a<<endl;
- cout<<"title: "<<t<<endl;
- cout<<"course: "<<c<<endl;
- }
- protected:
- string nam;
- char s;
- int a;
- string t;
- string c;
- };
- class student{
- public:
- student(string name,char sex,int age,int number, string department)
- {
- nam=name;
- s=sex;
- a=age;
- num=number;
- dep=department;
- }
- void print()
- {
- cout<<"name: "<<nam<<endl;
- cout<<"sex: "<<s<<endl;
- cout<<"age: "<<a<<endl;
- cout<<"number: "<<num<<endl;
- cout<<"department: "<<dep<<endl;
- }
- protected:
- string nam;
- char s;
- int a;
- int num;
- string dep;
- };
- class gradstudent:public student
- {
- public:
- gradstudent(string name,char sex,int age,int number, string department,string professor):student(name,sex,age,number,department)
- {
- pro=professor;
- }
- void print()
- {
- cout<<"name: "<<nam<<endl;
- cout<<"sex: "<<s<<endl;
- cout<<"age: "<<a<<endl;
- cout<<"number: "<<num<<endl;
- cout<<"department: "<<dep<<endl;
- cout<<"professor: "<<pro<<endl;
- }
- private:
- string pro;
- };
- int main()
- {
- cout<<"*********************************************"<<endl;
- cout<<"******欢迎您来到学生和教师档案查询系统*******"<<endl;
- cout<<"*********************************************"<<endl;
- int i,j;
- teacher teac("shishuaishuai",'m',27,"js","mxdx");
- student stu("liendan",'w',22,22,"computer");
- gradstudent grad("chengkai",'m',25,12,"computer","hzp");
- cout<<endl;
- loop:cout<<"请输入你要查询的对象:"<<endl<<"1.教师; 2.学生; 3.研究生。"<<endl;
- {
- cin>>i; if(i==1)teac.print();
- else if(i==2) stu.print();
- else grad.print();
- }
- cout<<endl;
- cout<<"是否继续查询? 1.yes 2.no"<<endl; cin>>j;
- if(j==1) goto loop;
- else return 0;
- }
程序输出结果如下:
4.试写出所能想到的所有形状(包括二维的和三维的),生成一个形状层次类结构。生成的层次结构一Shape作为基类,并由此派生出TwoDimShape类和ThreeDimShape类。它们的派生类是不同形状类,定义层次结构中的每一个类,并用函数main()进行测试。(sy5_4.cpp)
编写程序如下:
- //sy5_4.cpp
- #include<iostream>
- using namespace std;
- class Shape
- {
- public:
- Shape(){};
- double area() const{return 0.0;}
- double bulk() const{return 0.0;}
- };
- class TwoDimShape:public Shape{};
- class Circle:public TwoDimShape
- {
- public:
- Circle(double r){R=r;}
- double area()const{return 3.14*R*R;}
- protected:
- double R;
- };
- class ThreeDimShape:public Shape{};
- class sphere:public ThreeDimShape
- {
- public:
- sphere(double w){R=w;}
- double bulk()const{return 4/3*3.14*R*R*R;}
- protected:
- double R;
- };
- int main()
- {
- Shape sha;
- double area;
- double bulk;
- Circle c(7.0);
- area=c.area();
- cout<<"Area of circle is "<<area<<endl;
- sphere sph(9.0);
- bulk=sph.bulk();
- cout<<"Bulk of sphere is "<<bulk<<endl;
- return 0;
- }
程序输出结果如下:
分析与讨论
1.通过对实验内容中第1题的调试,总结不同继承方式的情况下,基类成员在派生类中的访问权限。
答:当类的继承方式为公有继承时,在派生类中,基类的公有成员和保护成员被继承后分别作为派生类的公有成员和保护成员,这样使得派生类的成员函数可以直接访问它们,而派生类成员函数无法直接访问基类的私有成员。在类的外部,派生了的对象可以访问继承下来的基类公有成员。 当类的继承方式为私有继承时,在派生类中,基类的公有成员和保护成员作为派生类的私有成员,派生类的成员函数可以直接访问它们,而派生类的成员函数无法直接访问基类的私有成员。在类外部,派生类的对象无法访问基类的所有成员。
当类的继承方式为保护继承时,在派生类中,基类的公有成员和保护成员作为派生类的保护成员,派生类的成员函数可以直接访问它们,而派生类的成员函数无法直接访问基类的私有成员。在类外部,派生类的对象无法基类的所有成员。
2.解释实验内容第2题的运行结果,总结多继承方式的情况下,构造函数与析构函数的调用时机与顺序。虚基类的构造函数与普通基类的构造函数在调用时有什么不同?
答:构造函数的调用顺序:先调用所有基类的构造函数,再调用派生类中子对象类的构造函数(如果派生类中没有子对象),最后调用派生类的构造函数。
虚基类的构造函数的调用顺序:遵循两个原则,而且按顺序优先满足:1 先调用完所以基类,再调用子类;2 先调用虚拟基类,再调用非虚拟基类,一旦调用了虚拟基类的构造函数,则非虚拟基类构造函数就按照声明的顺序被调用。
普通基类的构造函数:虚基类构造函数、普通基类构造函数、子类构造函数和其他(从左至右依次执行)。
3.如果希望附图1中的Base1、Base2均有两个,如何修改顺序?
实验总结:
通过本章的学习了解类的继承,三种继承方式的区别,以及通过参数列表对派生类的成员赋值的方法,即调用基类构造函数后再对新增成员赋值。