类的多态性的实现实验报告
实验名称:类的多态性的实现 学时安排:3
实验类别:设计性实验 实验要求:1人1组
目录
一、实验目的
1.理解重载运算符的意义。
2.掌握使用友元函数重载运算符的特点。
3.掌握重载运算符函数的调用方法。
4.掌握动态联编的概念。
5.掌握虚函数和纯虚函数的使用方法。
二、实验原理介绍
设计性实验
具体原理请见实验内容和步骤
实现对抽象类的继承,通过operator函数调用的形式,实现运算符的重载
三、实验设备介绍
软件需求: Visual Studio C++或Codeblocks或Dev C++或其他C++ IDE
硬件需求: 能够流畅运行C++ IDE的计算机一台。
四、实验内容
某大学班级有本科生CStudent、留学生FStudent,他们的绩点计算方法如下:本科生绩点计算方法是: 英语*0.03+数学*0.04+计算机*0.03。留学生绩点计算方法: 中文*0.03+数学*0.04+计算机*0.03。成绩均为百分制,绩点总计10。
每类学生都有学号、姓名、性别、年龄、类别等数据,类别分为本科生和留学生,各类人员使用统一接口getPoint()计算各类人员的绩点,重载<<运算符实现学生信息的输出。其次,设计一个统计并输出该班级每个人员绩点信息的报表类Report,该类提供insert接口向Report类的容器中添加学生信息,并提供print接口用于输出所有学生的学号、姓名、性别、年龄和绩点。为了方便实现查找功能,为Report类重载[]运算符的功能,下标值为学生类别,能根据类别查找出所有该类别的学生,并重载print接口,输出指定类别的学生信息。存储学生对象的容器请选用合适的STL容器。
初始成绩表里可以先固定添加4个本科生和4个留学生信息。各个指令和对应功能如下:
指令1:输入学号,删除相应记录;
指令2:删除所有学生的记录;
指令3:分类别显示所有学生绩点记录表;
指令4:输入类别,显示该类别学生绩点记录表;
指令-1:退出。
五、程序清单
5.1 头文件
(1) Student.h
#pragma once
#include <iostream>
#include <string>
using namespace std;
//学生类
class Student{
public:
Student(string id, string name, string sex, int age, string type, double math, double computer);
//构造函数
virtual ~Student() = default; //虚析构函数
virtual double getPoint() const=0; //计算绩点
virtual Student *clone() const = 0; //深拷贝
friend ostream &operator << (ostream &os, const Student &stu); //重载<<运算符
friend class Report;
protected:
string id_, name_, sex_, type_; //学号、姓名、性别、类别
int age_; //年龄
double math_, computer_; //数学成绩、计算机成绩
};
(2) CStudent.h
#pragma once
#include <iostream>
#include <string>
#include "Student.h"
using namespace std;
//本科生类
class CStudent:public Student {
public:
explicit CStudent(const string &id = "", const string &name = "", const string &sex = "男", int age = 18, const string &type = "本科生", double english = 0, double math = 0, double computer = 0); //构造函数
double getPoint() const override; //计算绩点
CStudent* clone() const override; // 深拷贝
friend istream &operator>>(istream &is, CStudent &student); //输入运算符重载
private:
double english_; //英语成绩
};
(3) FStudent.h
#pragma once
#include <iostream>
#include <string>
#include "Student.h"
using namespace std;
//留学生类
class FStudent:public Student{
public:
explicit FStudent(const string &id = "", const string &name = "", const string &sex = "男", int age = 18,const string &type = "留学生", double chinese = 0, double math = 0, double computer = 0); //构造函数
double getPoint() const override; //计算绩点
FStudent *clone() const override; // 深拷贝
friend istream &operator>>(istream &is, FStudent &student); //输入运算符重载
private:
double chinese_; //语文成绩
};
(4) Report.h
#pragma once
#include <iostream>
#include <iomanip>
#include <string>
#include <map>
#include "Student.h"
#include "CStudent.h"
#include "FStudent.h"
using namespace std;
//报表类
class Report{
public:
Report(); //默认构造函数
void insert(const Student &student); //插入新学生(左值)
void print(); //输出所有学生的学号、姓名、性别、年龄和绩点
void print(const string &type); //输出类型为type的学生的学号、姓名、性别、年龄和绩点
void erase(const string &id); //删除学号为id的学生记录
void clear(); //删除所有学生记录
auto operator[](const string &type) {return students_.equal_range(type);}
//重载[]运算符,寻找学生类型相同的数据范围
static void menu(); //菜单
private:
multimap<string, unique_ptr<Student>> students_; //存储所有学生信息
double minPoint_(const string &type) const; //计算所有学生中的最低绩点
double maxPoint_(const string &type) const; //计算所有学生中的最高绩点
};
5.2 源文件
(1) Student.cpp
#include <iostream>
#include <iomanip>
#include "Student.h"
using namespace std;
Student::Student(string id, string name, string sex, int age, string type, double math, double computer) :id_{id},name_{name},sex_{sex},age_{age},type_{type},math_{math},computer_{computer}{}
ostream &operator << (ostream &os, const Student &stu) {
os << setw(12) << left << stu.id_ << setw(12) << left << stu.name_
<< setw(12) << left << stu.sex_ << setw(12) << left << stu.age_
<< fixed << setprecision(2) << stu.getPoint();
return os;
}
(2) CStudent.cpp
#include <iostream>
#include "CStudent.h"
using namespace std;
CStudent::CStudent(const string &id, const string &name, const string &sex, int age, const string &type, double english, double math, double computer):Student(id, name, sex, age, type, math, computer),english_{english}{}
double CStudent::getPoint() const{
return english_ * 0.03 + math_ * 0.04 + computer_ * 0.03;
}
CStudent *CStudent::clone() const{
return new CStudent{*this};
}
istream &operator>>(istream &is, CStudent &student) {
string id, name, sex, type; int age; double english, math, computer;
is >> id >> name >> sex >> age >> english >> math >> computer;
student.id_ = id;
student.name_ = name;
student.sex_ = sex;
student.age_ = age;
student.english_ = english;
student.math_ = math;
student.computer_ = computer;
return is;
}
(3) FStudent.cpp
#include <iostream>
#include "FStudent.h"
using namespace std;
FStudent::FStudent(const string &id, const string &name, const string &sex, int age,const string &type, double chinese, double math, double computer):Student(id, name, sex, age, type, math, computer),chinese_{chinese}{}
double FStudent::getPoint() const {
return chinese_ * 0.03 + math_ * 0.04 + computer_ * 0.03;
}
FStudent *FStudent::clone() const {
return new FStudent{*this};
}
istream &operator>>(istream &is, FStudent &student) {
string id, name, sex, type; int age; double chinese, math, computer;
cin >> id >> name >> sex >> age >> chinese >> math >> computer;
student.id_ = id;
student.name_ = name;
student.sex_ = sex;
student.type_ = type;
student.age_ = age;
student.chinese_ = chinese;
student.math_ = math;
student.computer_ = computer;
return is;
}
(4) Report.cpp
#include <iostream>
#include <string>
#include "Report.h"
Report::Report() {
//初始成绩表里固定有4个本科生和4个留学生的信息
students_.insert(make_pair("本科生", new CStudent("001", "红小豆", "男", 18, "本科生", 90, 95, 94)));
students_.insert(make_pair("本科生", new CStudent("002", "李建勋", "男", 19, "本科生", 70, 65, 59)));
students_.insert(make_pair("本科生", new CStudent("003", "胡图图", "女", 19, "本科生", 84, 93, 85)));
students_.insert(make_pair("本科生", new CStudent("004", "派大星", "男", 20, "本科生", 71, 83, 67)));
students_.insert(make_pair("留学生", new FStudent("101", "Tom", "男", 19, "留学生", 82, 75, 84)));
students_.insert(make_pair("留学生", new FStudent("102", "Jerry", "女", 18, "留学生", 94, 72, 88)));
students_.insert(make_pair("留学生", new FStudent("103", "Lucy", "女", 19, "留学生", 91, 80, 69)));
students_.insert(make_pair("留学生", new FStudent("104", "Peter", "男", 20, "留学生", 80, 90, 100)));
}
void Report::insert(const Student &student){
students_.insert(make_pair(student.type_, student.clone()));
}
void Report::erase(const string &id){
for(auto i = students_.begin(); i != students_.end();)
{
if(i->second->id_ == id) i=students_.erase(i);
else ++i;
}
}
void Report::clear(){
students_.clear();
}
double Report::minPoint_(const string &type) const{
auto x = students_.equal_range(type);
double minPoint = INT_MAX;
for(auto i = x.first; i != x.second; ++i)
{
if (i->second->getPoint() < minPoint)
{
minPoint = i->second->getPoint();
}
}
return minPoint;
}
double Report::maxPoint_(const string &type) const {
auto x= students_.equal_range(type);
double maxPoint = 0;
for (auto i = x.first; i != x.second; ++i)
{
if (i->second->getPoint() > maxPoint)
{
maxPoint = i->second->getPoint();
}
}
return maxPoint;
}
void Report::print() {
cout << string(52, '-') << endl; //分割线
print("本科生"); //输出本科生信息
cout << string(52, '-') << endl;
print("留学生"); //输出留学生生信息
cout << string(52, '-') << endl;
}
void Report::print(const string &type){
cout << "类别:" << type << endl; //输出学生类别
cout << setw(13) << left << "学号" << setw(12) << left << "姓名"
<< setw(12) << left << "性别" << setw(14) << left << "年龄" << "绩点" << endl; //输出属性值
for (auto i = (*this)[type].first; i != (*this)[type].second; ++i)
{
cout << *i->second << endl; //输出该类所有学生信息
}
if ((*this)[type].first != (*this)[type].second)
{
cout << fixed << setprecision(2)
<< "最低绩点:" << minPoint_(type) << endl
<< "最高绩点:" << maxPoint_(type) << endl; //输出该类学生的最高、最低绩点
}
}
void Report::menu(){
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_RED);
cout << "--------------------------------------------------" << endl
<< " 操作菜单项 " << endl
<< " 输入1:输入学号,删除相应记录 " << endl
<< " 输入2:删除所有学生的记录 " << endl
<< " 输入3:分类别显示所有学生绩点记录表 " << endl
<< " 输入4:输入类别,显示该类别学生绩点记录表 " << endl
<< " 输入5:插入新学生信息 " << endl
<< " 输入-1:退出 " << endl
<< "--------------------------------------------------" << endl;
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE);
}
5.3 主函数
#include <iostream>
#include <string>
#include <Windows.h>
#include "Student.h"
#include "CStudent.h"
#include "FStudent.h"
#include "Report.h"
using namespace std;
int main(){
Report report;
int opt;
do{
Report::menu();
cin >> opt;
if (opt == 1)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_GREEN); //设置字颜色为绿色
cout << "请输入需要删除的学生学号:";
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE); //设置字颜色为白色
string id;
cin >> id;
report.erase(id);
}
else if (opt == 2)
{
report.clear();
}
else if (opt == 3)
{
report.print();
}
else if (opt == 4)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_GREEN); //设置字颜色为绿色
cout << "请输入需要查看学生绩点的类别:";
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE); //设置字颜色为白色
string type;
cin >> type;
report.print(type);
}
else if (opt == 5)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_GREEN); //设置字颜色为绿色
cout << "请输入需要添加的学生类型:" << endl;
string z;
cin >> z;
if (z == "本科生")
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_GREEN); //设置字颜色为绿色
cout << "请输入需要添加的学生信息(学号、姓名、性别、年龄、英语成绩、数学成绩、计算机成绩)" <<endl;
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE); //设置字颜色为白色
CStudent student;
cin >> student;
report.insert(student);
}
else if (z == "留学生" )
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_GREEN); //设置字颜色为绿色
cout << "请输入需要添加的学生信息(学号、姓名、性别、年龄、语文成绩、数学成绩、计算机成绩)" <<endl;
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY|FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE); //设置字颜色为白色
FStudent student;
cin >> student;
report.insert(student);
}
}
} while (opt != -1);
return 0;
}
六、运行结果
1、分类别显示所有学生绩点记录表
2、输入类别,显示该类别学生绩点记录表
3、输入学号,删除相应记录
4、删除所有学生的记录
5、插入新学生信息
七、实验心得
通过本次实验,我利用friend友元函数实现了输出运算符<<、数组下标运算符[]以及输入运算符>>的重载,使得运算符拥有了我们想要的功能需求;利用继承、virtual虚函数和指针完成了多态实现,如计算绩点时在CStudent、FStudent两个类中对基类的double getPoint()函数进行重写可以实现不同的运算效果。同时,我也熟悉了纯虚函数的使用,明白了其本身是没有意义的,当其被各个派生类继承下来时有了不同的含义。
另外,我对c++中STL迭代器有了进一步的认识。由于本次实验我们需要区分学生类型进行分类输出,所以选择multimap容器,将学生类型作为主键进行存储和操作(multimap容器保存有序的键/值对,可以保存重复的元素)。在实验中,利用了迭代器本身拥有的eraser(),clear(),insert()实现删除、清空、插入操作,利用equal_range(type)实现对容器内type键的区间查找(返回值为一对迭代器,返回first和last之间等于type的元素区间)。