1.题目
设计一个学生管理系统,要求如下:
- 学生来自英语系,数学系、计算机系,每个学生的公共信息部包括:学号、姓名、年龄、系别,高数成绩、英语成绩、体育成绩;英语系学生有综合英语、口语等课程成绩;数学系学生有实变函数、泛函分析、微分几何等课程成绩;计算机系学生有计算机组成原理、计算机体系结构、汇编语言等课程成绩。每个学生可以查询相应信息(学号、姓名、年龄、系别及成绩信息);可以查询各门课程的平均成绩。用类及类的派生实现上述功能,要求使用类的集成、虚函数、虚基类的C++语言特性;要求每个类采用不同的方式实现构造函数
- 设计一个学生管理类,其中包含可变学生数目,要求实现:
1)初始化,所有学生的信息组织以链表方式实现
2)实现指定学生的信息查询(如利用学号、姓名等方式)
3)按照公共课平均成绩排序
4)按照学生所有课程的平均成绩排序
5)增加学生
6)删除指定学生 - 注意构造函数和析构函数的实现方式。每个类的声明需放在单独的头文件中(即一个类的声明放在一个后缀名为h的文件中),每个类的实现放在一个单独cpp文件中;测试程序(即包含 main函数的程序)单独放在一个cpp文件中
2.总体分析
首先有一个基类workstuclass.h,存放有三个系的学生的公共信息,以及一个虚函数的第二级菜单还有公共信息的输入输出函数,其函数在另一个文件workstufun.cpp文件里面定义。然后是三个系的学生类的公有继承学生基类,分别是workcomclass.h、workengclass.h和workmatclass.h,三个类里面除了有各个系学生的非公有信息以外,还有其对应的各个操作函数,而函数的定义是放在对用的.cpp文件里面。紧接着是一个学校类workschclass.h,它继承了计算机、英语和数学这三个系的派生类而来,里面含有一个第一级菜单,同样也是在另一个文件.cpp文件里面定义。最后就是主函数了,定义了一个School学校类的对象,用来调用第一级菜单完成指定操作。总的来说,就是首先调用第一级菜单,输入指定指令进入相应的系的学生管理系统后调用第二级菜单完成相应的添加信息、删除信息等基本操作,三个不同系的信息放在不同的链表并储存在三个不同的文件里面。
3.详细设计
由于计算机系、英语系以及数学系通过第一级菜单进入第二级菜单后其基本操作都相同,不同的只是变量不同而已,所以这里只介绍了计算机系的函数以及它们之间的调用关系。
首先学生基类的定义,用#ifndef WORKSTUCLASS_H_ 、 #define WORKSTUCLASS_H_和#endif来避免重复定义,接下来是函数体了,该基类比较简单,里面只有定义了几个共有变量,以及三个函数,一个是第二级操作菜单,定义成虚函数(其实本人觉得在这个程序当中并没有需要去定义虚函数,但是为了满足作业要求,所以就定义了一个菜单虚函数),然后还有公共信息的输入输出函数,三个函数的定义均放在它的.cpp文件workstufun.cpp文件里面,首先它的.cpp文件还是要包含该类文件,下面就是其输入输出函数的代码。
void Student::input1()//输入1
{
cout<<"学号:"<<endl;
cin>>id;
cout<<"系别:"<<endl;
cin>>faculty;
cout<<"姓名:"<<endl;
cin>>name;
cout<<"年龄:"<<endl;
cin>>age;
cout<<"高数成绩:"<<endl;
cin>>math_score;
cout<<"英语成绩:"<<endl;
cin>>english_score;
cout<<"体育成绩:"<<endl;
cin>>sport_score;
}
void Student::output1()//输出1
{
cout<<"学号:"<<id<<endl;
cout<<"系别:"<<faculty<<endl;
cout<<"姓名:"<<name<<endl;
cout<<"年龄:"<<age<<endl;
cout<<"高数成绩:"<<math_score<<endl;
cout<<"英语成绩:"<<english_score<<endl;
cout<<"体育成绩:"<<sport_score<<endl;
}
接着是计算机系学生类的定义以及其各个操作函数的声明,首先同样的还是需要用#ifndef WORKCOMCLASS_H_、#define WORKCOMCLASS_H_以及#endif来避免重复定义,并将
并用#include "workstuclass.h"将基类头文件workstuclass.h也包含进来,然后声明各个操作函数并定义该类特有的变量以及为接下来算平均分的所定义的变量平均数,还有文件输入输出流以及该类的头尾指针和指向下一个结点的类指针。
virtual void menu2(); //计算机系菜单
void input2(); //计算机系输入
void output2(); //计算机系输出
void add(); //增加
void display(); //显示
void find(); //查询
void modify(); //修改
void del(); //删除
void save(); //保存信息到文件
void readfile(ifstream &in); //从文件读入数据到结点
void copy1(Comstu *p1,Comstu *p2);//交换相邻结点的数据
void sort(); //排序部分
void open(); //打开文件
void close(); //关闭文件
完后就是在另一个.cpp文件里面定义该类所声明的函数,还是要在开头把该类的文件包含进来。然后就是在基类当中定义的菜单虚函数,在介绍之前我想先介绍打开文件的函数以及关闭文件的函数。打开文件其实就是读一下文件,如果没有的话就创建文件。首先要先初始化三个类指针,然后利用
ifstream in("comstu.txt");
char ch = in.get();
if(in.eof())
{
cout<<"文件为空!"<<endl;
}
//若文件非空
while(!in.eof())
{
end->readfile(in);
if(end->name[0] == '\0')
break;//不加这一句的话程序会把文件最后结束的标记也都出来 end->next = new Comstu;
end = end->next;
}
来读文件,而其中readfile()其实就是自己写的读文件函数,就是用文件输入流in直接将全部数据读入链表(内存)便于接下来对数据操作的执行。然后就是文件关闭的函数的close(),然后首先在该函数的开头调用自己所写的保存数据至文件的函数,该函数首先打开文件,定义一个类指针,并将其指向第一个存有信息的结点,并利用for循环从此循环开始一直到尾结点,将其所有数据利用文件输出流out输出至文件,最后就是利用out.close()关闭文件即可(注意一下这里的关闭文件和自己所定义的关闭文件是不一样的,虽然叫法是一样的)。回到自己所写的关闭文件上,保存文件之后就是将自己所定义的指针利用delete函数逐个释放掉即可。
回到该计算机系学生类的菜单,首先利用所写的打开文件以及关闭文件放在菜单函数的头和尾部,这样在每次操作其对应的操作函数返回该菜单时就会自动保存,而没必要在每个函数里面写打开保存文件的操作。接着就是简单的输入输出函数,但和基类输入输出函数不同的就是在这个函数里面调用其输入输出函数并加入这个类特有的数据。后面的函数和上一次作业的函数基本一样。接着是增加结点至链表尾部的函数,利用尾指针指向输入函数来调用它,并为尾指针的指向下一个指针的指针新配置一块内存,并将其再次赋给尾指针,然后还有就是输入指令是否继续添加结点了。
接着是显示所有信息,定义一个类指针,并将其指向第一个含有信息的结点,同时利用循环并在循环里面调用输出函数逐个将结点信息输出至屏幕。然后就是查询功能,分为按学号或者是按姓名查询,首先输出一个菜单并输入指令以及判断指令的正确性,然后同样的定义一个类指针指向头结点的next指针(也就是第一个含有信息的指针),然后输入学号或者是名字后直接利用循环从该链表的第一个信息结点开始逐个判断所输入的信息,找到后直接调用输出函数将其显示到屏幕即可,并将标记变量设置为1,完后判断标记变量是否为1,如果为非1则输出未找到该学生信息,然后继续询问是否继续查询,继续查询则直接返回循环前面重复上面的步骤,如果不查询则利用break跳出循环。
接下来解释修改部分,输入所要修改的的学生名字,定义类指针,从头信息结点利用for(pos;pos!=end;pos=pos->next)开始查找,找到后调用输入信息函数重新开始写这个结点的信息,之后输出修改成功的信息。然后是删除结点,首先定义判断是否找到所要删除的结点的标志变量,输入想要删除的学生名字,然后就是将头结点指针以及第一个信息结点指针赋给另外定义的类指针p2和p1,还是同样的利用循环从头开始查找,找到该删除的结点后将判断变量设为1,然后将p1的next指针赋给p2,然后利用delete删除p1,利用break 跳出循环,最后利用判断变量判断是否找到该学生。
接着最后是排序部分,首先定义一个函数来将一个结点信息复制到另一个结点上,其实函数里面就是简单的复制操作。然后是正式的排序部分,首先是输出一个排序菜单,输入指令后执行后执行相应的操作,首先定义两个类指针,并将其中一个指向第一个信息结点,比如输入的指令是1执行按全部课程平均成绩降序排的功能,然后利用冒泡排序,从头信息结点开始逐个判断相邻结点的平均分大小,若后一个结点大于前面一个结点,那么利用自己写的交换结点函数进行交换两个结点的信息,注意不是交换指针,至此该计算机系学生类的函数函数定义已全部完成。
接下来就是一个继承了三个系的类的School类,并采用虚基类的共有继承防止继承了多个基类进而出错,该类里面只声明了一个第一级菜单,并在对应的.cpp文件里面进行定义,而这个菜单通过虚函数的方式调用三个系的类的在基类声明并在各个系里面分别定义的第二级菜单menu2(),代码如下,接下来利用对应的指针进行指向调用即可。
Comstu c1;
Engstu e1;
Matstu m1;
Student *p1=&c1;
Student *p2=&e1;
Student *p3=&m1;
最后就是主函数部分了,其实就是简单的几行代码,包含定义的学校类worrkschclas.h这个头文件,然后就是定义学校类的对象来调用它里面定义的第一级菜单函数即可,至此,整个代码解释结束。
4.源码
https://github.com/zhuhezhang/student-achievement-management-system-2ed
https://gitee.com/zhuhezhang/student-achievement-management-system-2ed