【实验目的】
1.理解软件重用性的一种形式——继承。
2.能够通过继承已有的类创建新类。
3.理解基类和派生类的概念。
4.能够在派生类中使用构造函数和析构函数
5.学习虚基类在解决二义性问题中的作用。
6.熟悉多态分类,理解静态联编和动态联编概念。
7.掌握运算符重载方法。
【实验内容】
1.分析程序,写出下列程序的运行结果:
(1) #include<iostream.h>
class Base
{
private:
int base_priv_dat;
protected:
int base_prot_dat;
public:
void base_show();
};
class Derived:public Base
{
private:
int derived_priv_dat;
public:
void derived_show();
};
void Base::base_show()
{
base_priv_dat=1; //基类函数可以操纵私有和保护型基类数据
base_prot_dat=2;
cout<<"base_priv_dat="<<base_priv_dat<<'\t'
<<" base_prot_dat="<<base_prot_dat<<endl;
}
void Derived::derived_show()
{
derived_priv_dat=3;
base_prot_dat=4; //派生函数可以处理保护型基类数据,但不能处理私有基类数据
cout<<"derived_priv_dat="<<derived_priv_dat<<'\t'
<<"base_prot_dat="<<base_prot_dat<<endl;
base_show(); //派生函数可以调用公有基类函数
}
int main()
{
Derivedd_obj;
d_obj.base_show(); //可用派生对象调用基类函数
d_obj.derived_show();
return(0);
}
定义派生对象,分别调用基类函数base_show()和派生类函数derived_show();基类函数base_show()把1和2分别赋值给其私有数据成员base_priv_dat和保护数据成员base_prot_dat然后将其输出;
而派生类函数derived_show()把3和4分别赋值给其私有数据成员derived_priv_dat和基类的保护数据成员base_prot_dat,然后将其输出;最后调用基类函数base_show()将其私有数据成员base_priv_dat和保护数据成员base_prot_dat输出;
(2) #include<iostream.h>
#include<string.h>
class Person
{
public:
Person(const char* s) //带参数的构造函数
{
name=newchar[strlen(s)+1];
strcpy(name,s);
}
~Person(){delete []name;} //析构函数做清理工作
char *GetName(){return name;}
protected:
char *name;
};
class Student:public Person
{
char*major;
public:
Student(constchar * s,const char *m):Person(s) //派生类构造函数
{major=newchar[strlen(m)+1];strcpy(major,m);}
~Student(){delete[]major;} //派生类析构函数
char*GetMajor(){return major;}
};
int main()
{
Studentstu("WZQ","Electric automatization");
cout<<"studentName is:"<<stu.GetName()<<" Major is:"
<<stu.GetMajor()<<endl;
return(0);
}
定义派生对象stu同时将参数”WZQ"和"Electric automatization”分别传给字符型指针s和m,再通过s将参数”WZQ"传给基类person并将其赋值给Name;而参数"Electric automatization”则通过派生类函数传给Major;然后通过派生对象stu调用基类函数GetName()和派生类函数GetMajor()将Name和Major显示出来;
2.编译运行下列程序,分析出现编译错误的原因,并给出解决办法。
#include<iostream>
#include<string>
using namespace std;
class Person
{
public:
Person(stringthe_name,string the_gender,string the_id)
{
name=the_name;
gender=the_gender;
id_number=the_id;
cout<<"Person类构造函数被调用!"<<endl;
}
voidshow()
{
cout<<"姓名:"<<name<<endl;
cout<<"性别:"<<gender<<endl;
cout<<"身份证号码:"<<id_number<<endl;
}
protected:
stringname; //姓名
stringgender; //性别
stringid_number; //身份证号码
};
class Student: public Person
{
public:
Student(stringn,string g,string i,string c):Person(n,g,i)
{
classname=c;
cout<<"Student类构造函数被调用!"<<endl;
}
protected:
stringclassname; //学生所在班级
};
class Teacher: public Person
{
public:
Teacher(stringn,string g,string i,string d):Person(n,g,i)
{
department=d;
cout<<"Teacher类构造函数被调用!"<<endl;
}
protected:
stringdepartment; //教师所在单位
};
class Assistant:public Student,public Teacher
{
public:
Assistant(stringn,string g,string i,string c,string d):Student(n,g,i,c),Teacher(n,g,i,d)
{}
voiddisp()
{
show();
cout<<"班级:"<<classname<<endl;
cout<<"系部:"<<department<<endl;
}
};
int main()
{
Assistant a("李玮","男","420300199212032323","软件141","电信系");
a.disp();
return 0;
}
解决方法一:(通过作用域符号来唯一标识)
#include<iostream>
#include<string>
using namespace std;
class Person
{
public:
Person(stringthe_name,string the_gender,string the_id)
{
name=the_name;
gender=the_gender;
id_number=the_id;
cout<<"Person类构造函数被调用!"<<endl;
}
voidshow()
{
cout<<"姓名:"<<name<<endl;
cout<<"性别:"<<gender<<endl;
cout<<"身份证号码:"<<id_number<<endl;
}
protected:
stringname; //姓名
stringgender; //性别
stringid_number; //身份证号码
};
class Student: public Person
{
public:
Student(stringn,string g,string i,string c):Person(n,g,i)
{
classname=c;
cout<<"Student类构造函数被调用!"<<endl;
}
protected:
stringclassname; //学生所在班级
};
class Teacher: public Person
{
public:
Teacher(stringn,string g,string i,string d):Person(n,g,i)
{
department=d;
cout<<"Teacher类构造函数被调用!"<<endl;
}
protected:
stringdepartment; //教师所在单位
};
class Assistant:public Student,public Teacher
{
public:
Assistant(stringn,string g,string i,string c,string d):Student(n,g,i,c),Teacher(n,g,i,d)
{}
voiddisp()
{
Teacher::show();//或者是Student::show()
cout<<"班级:"<<classname<<endl;
cout<<"系部:"<<department<<endl;
}
};
int main()
{
Assistant a("李玮","男","420300199212032323","软件141","电信系");
a.disp();
return 0;
}
解决方法二:(虚基类技术)
#include<iostream>
#include<string>
usingnamespace std;
classPerson
{
public:
Person(string the_name,stringthe_gender,string the_id)
{
name=the_name;
gender=the_gender;
id_number=the_id;
cout<<"Person类构造函数被调用!"<<endl;
}
void show()
{
cout<<"姓名:"<<name<<endl;
cout<<"性别:"<<gender<<endl;
cout<<"身份证号码:"<<id_number<<endl;
}
protected:
string name; //姓名
string gender; //性别
string id_number; //身份证号码
};
classStudent:virtual public Person
{
public:
Student(string n,string g,string i,stringc):Person(n,g,i)
{
classname=c;
cout<<"Student类构造函数被调用!"<<endl;
}
protected:
string classname; //学生所在班级
};
classTeacher:virtual public Person
{
public:
Teacher(string n,string g,string i,stringd):Person(n,g,i)
{
department=d;
cout<<"Teacher类构造函数被调用!"<<endl;
}
protected:
string department; //教师所在单位
};
classAssistant:public Student,public Teacher
{
public:
Assistant(string n,string g,string i,stringc,string d):Person(n,g,i),Student(n,g,i,c),Teacher(n,g,i,d)
{}
void disp()
{
show();
cout<<"班级:"<<classname<<endl;
cout<<"系部:"<<department<<endl;
}
};
intmain()
{
Assistant a("李玮","男","420300199212032323","软件141","电信系");
a.disp();
return 0;
}
4.开发一个简单的大学人员管理程序,该程序可以管理大学的一些基本人员:学生(student)、教师(teacher)。首先设计一个虚基类person。通过该类保存人员的最基本信息:姓名(name)、年龄(age)、性别(sex)和身份证号码。然后使用该类派生出学生类student、教师类teacher,在其中添加各自的特性,如在student类中添加如下信息:专业(speciality),在teacher类中添加院系(department)等。还有部分教师在工作的同时在职修读学位,因此同时具有教师和学生双重身份,所以由student类和teacher类再次派生出stuTeacher类。为每个类定义一个输出函数print(),输出该类相关信息。
程序代码如下:
#include "iostream"
#include "string"
using namespace std;
class Person
{
private: string m_Name;
string m_Sex;
int m_Age;
public:
Person(){}
Person(stringname, string sex, int age)
{
m_Name= name;
m_Sex= sex;
m_Age= age;
}
stringPerson_Get_Name()
{
returnm_Name;
}
stringPerson_Get_Sex()
{
returnm_Sex;
}
intPerson_Get_Age()
{
returnm_Age;
}
};
class Student :virtual public Person
{
private: string m_Speciality;
public:
Student(){}
Student(stringname, string sex, int age,string speciality):Person(name,sex,age)
{
m_Speciality= speciality;
}
~Student()
{
}
stringStudent_Get_Speciality()
{
returnm_Speciality;
}
voidStu_print()
{
cout<<"学生信息:"<< endl;
cout<<"姓名:"<< Person_Get_Name()<< endl;
cout<<"性别: " <<Person_Get_Sex()<< endl;
cout<<"年龄: " <<Person_Get_Age()<< endl;
cout<<"专业: "<<Student_Get_Speciality() << endl<< endl;
}
};
class Teacher :virtual public Person
{
private: string m_Department;
public:
Teacher(){}
Teacher(stringname, string sex, int age, string department) :Person(name, sex, age)
{
m_Department= department;
}
~Teacher()
{
}
stringTeacher_Get_Department()
{
return m_Department;
}
voidTea_print()
{
cout<<"老师信息:" << endl;
cout<<"姓名:"<< Person_Get_Name()<< endl;
cout<<"性别: " <<Person_Get_Sex()<< endl;
cout<<"年龄: " <<Person_Get_Age()<< endl;
cout<<"学院: "<<Teacher_Get_Department()<< endl<< endl;
}
};
class StuTeacher :public Student,publicTeacher
{
public:
StuTeacher(){}
StuTeacher(stringname, string sex,int age, stringspeciality,string department):Person(name,sex,age),Student(name,sex,age,speciality),Teacher(name, sex, age, department)
{
}
~StuTeacher()
{
}
voidStu_Tea_print()
{
cout<<"在读老师信息:" << endl;
cout<<"姓名:"<< Person_Get_Name()<< endl;
cout<<"性别: " <<Person_Get_Sex()<< endl;
cout<<"年龄: " <<Person_Get_Age()<< endl;
cout<<"专业: "<<Student_Get_Speciality() << endl;
cout<<"学院: "<<Teacher_Get_Department()<< endl;
}
};
int main()
{
Studentstudent(" 李玮","男", 22, "电气工程及其自动化");
student.Stu_print();
Teacherteacher("王老师","女", 33, "科技学院");
teacher.Tea_print();
StuTeacher stutracher("王伟", "男", 38," 计算机专业", "科技学院");
stutracher.Stu_Tea_print();
return0;
}
5. 用运算符重载设计复数类,实现复数的+、-、*、/运算。
#include <iostream>
#include <iomanip>
using namespace std;
class Complex
{
public:
Complex();
Complex(double r,double i);
Complex operator+(Complex &c2);
Complex operator-(Complex &c2);
Complex operator*(Complex &c2);
Complex operator/(Complex &c2);
void display();
private:
double real;
double imag;
};
//************************************************************
Complex::Complex(double r,doublei):real(r),imag(i){}//复数相加:(a+bi)+(c+di)=(a+c)+(b+d)i.
Complex Complex::operator+(Complex&c2)
{
returnComplex(real+c2.real,imag+c2.imag);
}
//复数相减:(a+bi)-(c+di)=(a-c)+(b-d)i.
Complex Complex::operator-(Complex&c2)
{
returnComplex(real-c2.real,imag-c2.imag);
}
//复数相乘:(a+bi)(c+di)=(ac-bd)+(bc+ad)i.
Complex Complex::operator*(Complex&c2)
{
returnComplex(real*c2.real-imag*c2.imag,real*c2.imag+imag*c2.real);
}
//复数相除:(a+bi)/(c+di)=(ac+bd)/(c^2+d^2)+(bc-ad)/(c^2+d^2)i
Complex Complex::operator/(Complex&c2)
{
returnComplex((real*c2.real+imag*c2.imag)/(c2.real*c2.real+c2.imag*c2.imag),(imag*c2.real-real*c2.imag)/(c2.real*c2.real+c2.imag*c2.imag));
}
void Complex::display()
{
cout<<""<<real<<"+"<<imag<<"i"<<endl;
}
int main()
{
double real,imag;
cout<<"请输入第一个复数的实部和虚部:"<<endl;
cin>>real>>imag;
Complex c1(real,imag);
cout<<"请输入第二个复数的实部和虚部:"<<endl;
cin>>real>>imag;
Complex c2(real,imag);
Complex c3=c1+c2;
cout<<"c1+c2=";
c3.display();
c3=c1-c2;
cout<<"c1-c2=";
c3.display();
c3=c1*c2;
cout<<"c1*c2=";
c3.display();
c3=c1/c2;
cout<<"c1/c2=";
c3.display();
return 0;
}
【思考题】
1.组合与继承的区别是什么?
2.公有继承、保护继承和私有继承分别在什么情况下使用?
3.什么是虚基类?有何作用?
4.静态联编和动态联编有什么区别?
5.简述空的虚函数与纯虚函数的区别?
6.简述抽象类和具体类的区别?
1.
答:1)组合关系可以显式地获得被包含类的对象,而继承则是隐式地获得父类的对象,被包含类和父类对应,而组合外部类和子类对应
2)组合关系在运行期决定,而继承关系在编译期就已经决定了。
3)组合是在组合类和被包含类之间的一种松耦合关系,而继承则是父类和子类之间的一种紧耦合关系。
4)当选择使用组合关系时,在组合类中包含了外部类的对象,组合类可以调用外部类必须的方法,而使用继承关系时,父类的所有方法和变量都被子类无条件继承,子类不能选择。
5)最重要的一点,使用继承关系时,可以实现类型的回溯,即用父类变量引用子类对象,这样便可以实现多态,而组合没有这个特性。
2.
答:1)公有继承时基类中各成员属性保持不变,基类中private成员被隐藏。派生类的成员只能访问基类中的public/protected成员,而不能访问private成员;派生类的对象只能访问基类中的public成员。
2)私有继承时基类中各成员属性均变为private,并且基类中private成员被隐藏。派生类的成员也只能访问基类中的public/protected成员,而不能访问private成员;派生类的对象不能访问基类中的任何的成员。
3)保护继承时基类中各成员属性均变为protected,并且基类中private成员被隐藏。派生类的成员只能访问基类中的public/protected成员,而不能访问private成员;派生类的对象不能访问基类中的任何的成员。
3.
答:为了类的继承在派生类继承基类时,加上一个virtual关键词则为虚拟基类继承。
作用:虚基类主要解决在多重继承时,基类可能被多次继承,虚基类主要提供一个基类给派生类。
4.
答:静态联编说的是在编译时就已经确定好了调用和被调用两者的关系。
5.
答:1)纯虚函数只有定义,没有实现;而虚函数既有定义,也有实现的代码。
纯虚函数一般没有代码实现部分。
2)包含纯虚函数的类不能定义其对象,而包含虚函数的则可以。
6.
答:
抽象类是不可被实例化的类,即它可以没有直接实例,这既可能是因为它的描述是不完整的(如缺少一个或多个操作的方法),也可能是因为即使它的描述是完整的它也不想被实例化。抽象类是为了以后说明。抽象类必须有可能含有实例的后代才能使用,一个抽象的叶类是没用的(它可以作为叶在框架中出现,但是最终它必须被说明)。
具体类可以没有任何抽象操作(否则,它必为抽象的),但是抽象类可以有具体操作。具体操作是可以被实现一次并在所有子类中不变地使用的操作。在它们的实现中,具体操作可以只使用声明它们的类所知道的特征(属性和操作)。