设计目的
- 使学生进一步理解和掌握课堂上所学的面向对象C++编程知识,巩固和加深学生对C++面向对象课程的基本知识的理解和掌握。
- 掌握C++面向对象编程和程序调试的基本技能,学会利用C++语言进行基本的软件设计,着重提高运用C++面向对象语言解决实际问题的能力。
- 掌握书写程序设计说明文档的能力,使学生学会使用各种计算机资料和查阅有关参考资料解决问题的方法。
具体要求
运用C++语言描述学生类,每一个类应包含数据成员和成员函数。学生的信息包括学号、姓名、性别、班级、高数成绩和英语成绩。注重面向对象程序设计理论知识的理解与实际的动手编程能力,要求学生设计具有继承与派生以及多态性的类,理解面向对象程序设计的核心的概念。
本题目要实现的主要功能如下:
- 建立基类---学生类,数据成员包括学号、姓名、性别;
- 派生类---电信类,新增数据成员有:班级、高数成绩、英语成绩;
- 能够实现对电信类学生信息的录入、修改、查找、排序、删除等功能;
- 能够输出总成绩,即高数和英语两门课的成绩之和;
- 能够对学生信息按高数成绩从高到低进行排序,若高数成绩一样,则按英语成绩排序,若成绩都一样,则按学号进行排序;
- 查找功能支持姓名查找或学号查找
- 若通过动态分配内存的链表或哈希表实现
设计思路
首先数据都是放在类中的,在创建一个结构体进行类与链表的结合使用完成学生信息管理系统。
分别在类与链表结构体中封装对象指针,方便之后形成链表结点对其进行控制。每个功能函数都被封装在链表结构体中,因为需要使用内部的指针,这样比较方便。
我采用的是带头双向链表来实现,这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了。链表为空时,也会有一个哨兵位的头结点(head),这样我们每次去实现功能的时候,不需要遍历。直接让当前结点指向头结点就可以从头开始依次下去,这也是我比较喜欢的一点。
主函数(main)中是实现这些功能的,但需要先创建一个头结点(空的),然后录入的时候进行结点之间的链接即可。
通过程序主界面写一个菜单,然后通过分支函数switch进行选择。
我认为类和链表的最大难点只需要想通它们之间的指针关系即可,只要理解了指针指向,就可以通过指针实现所有功能。
1. 录入信息
通过for循环可以控制输入的人数,这样想要插入多个学生数据时不用每次都调用Input()函数;在for循环中每次插入数据之前要记得开辟空间,更新指针的指向,录入信息是以后所有功能的基础,如果这步出错,接下来的所有功能实现都会有问题,特别是打印,录入成功但是什么都没打印出来。所以需要尤为注意!
我这里使用的是头插,所以每次扩容之后,都会present->pre=NULL;
插入信息之后,此时就顺势将英语成绩与高数成绩相加算出总分,之后打印直接打印总分会方便很多。
每次输入都会new一个新的类出来,不插入就不用开辟空间,之前也没有定了空间大小,我觉得这也是一种动态开辟的方法,而且简单易懂还很方便。
2.浏览信息
浏览所有信息,就是将所有信息打印出来。因为格式与录入的不一样,我使用了setw函数可以让表格对齐观感更好(注意使用要包头文件iomanip)。将表头信息封装成了display函数,在以后需要打印的时候直接调用,代码会更加整洁。因为链表中不是只有一组数据(一个结点),所以想要打印全部的数据需要使用while循环,然后更新指针指向即可。
3.查找信息
可以通过学生姓名与学生学号进行查找,只需要从头开始present=head;然后当想要查找的姓名或学号等于present-》Name与present->num即可
我将查找的函数封装成了bool类型的(其余都是void),方便接下来的删除和修改模块。因为想要删除和修改也需要在链表中找到相关的数据才可以进行相关操作,需要的时候直接调用就行,很方便。
因为通过姓名和学号是两种不同的方式(但是底层实现都一样),所以我又另外写了一个选择查找通过switch进行不同的选择。
4.删除信息
这个功能模块,说实话不是很难,但是我卡了一天,原因是我的录入模块一开始没有写好,导致它只能删除最后插入的数据,也就是头结点。浪费了我很多时间。所以说录入是基础!!!
当删除的是头结点和不是头结点的有两种处理方式
5.修改信息
通过Check函数查找想要修改的学生学号,找到重新输入即可。
6.排序
通过简单的冒泡排序实现。当当前结点比下一个结点大就交换。
因为按实训要求,需要对学生信息按高数成绩从高到低进行排序,若高数成绩一样,则按英语成绩排序,若成绩都一样,则按学号进行排序。所以直接嵌套就行。
之前我觉得代码太过凌乱了,想着封装成三个函数,然后达到条件之后依次调用,但是真正实现的时候,可以排序成功,但是一旦进入在排序中调用另一个函数,代码就不接着往下跑了,终止main函数中的循环了。我觉得是因为代码嵌套程度太高程序奔溃了,所以我就按一个函数的方式写了。虽然看着有些复杂,但是不会出错。
完整代码
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;
class Student
{
public:
int num;
string name;
string sex;
};
//电信类中存放学生数据
class DX :public Student
{
public:
int classroom;
int hmath;
int eng;
int sum;//总分
DX *next;//电信类指针,指向下一结点
DX* pre;//指向前一个结点
};
//双向链表
struct Linklist
{
DX* head;//头指针
DX *present;//当前指针
void Input();//录入
void Creat();//扩容
bool Check();//查找学号
bool Check1();//查找姓名
void SelectCheck();//选择查找
void Output();//浏览
void Delete();//删除
void Modify();//修改
void Sort();//排序,高数,英语,学号
}X;
//开辟新空间,new一个类出来
void Linklist::Creat()
{
//头插
present = new DX;//当前指针为新开辟的这个电信类
head = present;
present->next = NULL;
present->pre = NULL;
//此时只有一个结点
}
void Linklist::Input()
{
int size;//想要输入的学生个数
cout << "请输入学生的人数:";
cin >> size;
//有几个人循环几次
for (int i = 0; i < size; i++)
{
//在插入的时候就进行链表的链接
present = new DX;
head->pre = present;
present->next = head;
head = present;
//相当于头插
present->pre = NULL;
cout << "请输入第" << i + 1 << "个学生的学号:";
cin >> present->num;
cout << "请输入第" << i + 1 << "个学生的姓名:";
cin >> present->name;
cout << "请输入第" << i + 1 << "个学生的性别:";
cin >> present->sex;
cout << "请输入第" << i + 1 << "个学生的班级:";
cin >> present->classroom;
cout << "请输入第" << i + 1 << "个学生的高数成绩:";
cin >> present->hmath;
cout << "请输入第" << i + 1 << "个学生的英语成绩:";
cin >> present->eng;
present->sum = present->hmath + present->eng;
cout << "-----------------------------" << endl;
}
cout << "录入成功!!!!" << endl;
}
//打印表头信息
void display()
{
cout << setw(30) << "学生成绩信息" << endl; // setw函数设置字段宽度,需要引用头文件iomanip
cout << " --------------------------------------------------------------------------" << endl;
cout << setw(6) << "学号" << setw(12) << "姓名" << setw(8) << "性别" << setw(8)
<< "班级" << setw(8) << "高数" << setw(8) << "英语" << setw(12) << "总分" << endl;
cout << " --------------------------------------------------------------------------" << endl;
}
void Linklist::Output()
{
//从头开始
present = head;
display();
//打印出所有信息,直到末尾
while (present->next != NULL)
{//setw函数是用来设置行间距的
cout << setw(6) << present->num << setw(12) << present->name << setw(8) << present->sex
<< setw(8) << present->classroom << setw(8) << present->hmath << setw(8) << present->eng
<< setw(12) << present->sum << endl;
cout << " --------------------------------------------------------------------------" << endl;
present = present->next;//更新
}
}
//精确查找
//要想用学生姓名查找将num改为name即可 int变为string 类型要匹配
bool Linklist::Check()
{
present = head;
int id;
cin >> id;
display();
while (present->next != NULL)
{
if (id == present->num)
{
//找到打印出信息
cout << setw(6) << present->num << setw(12) << present->name << setw(8) << present->sex
<< setw(8) << present->classroom << setw(8) << present->hmath << setw(8) << present->eng
<< setw(12) << present->sum << endl;
cout << " --------------------------------------------------------------------------" << endl;
return true;
}
else
{
//不同就接着找
present = present->next;
}
}
cout << "查无此人!!!!" << endl;
return false;
}
bool Linklist::Check1()
{
present = head;
//cout << "请输入需要查找的学生学号:";
string nam;
cin >> nam;
display();
while (present->next != NULL)
{
if (nam == present->name)
{
//找到打印出信息
cout << setw(6) << present->num << setw(12) << present->name << setw(8) << present->sex
<< setw(8) << present->classroom << setw(8) << present->hmath << setw(8) << present->eng
<< setw(12) << present->sum << endl;
cout << " --------------------------------------------------------------------------" << endl;
return true;
}
else
{
//不同就接着找
present = present->next;
}
}
cout << "查无此人!!!!" << endl;
return false;
}
//选择查找
void Linklist::SelectCheck()
{
cout << "1.按学号查询" << endl;
cout << "2.按姓名查询" << endl;
//cout << "3.按姓名进行模糊查询" << endl;
cout << "请选择:";
int a;
cin >> a;
switch (a)
{
case 1:
cout << "请输入需要查找的学生学号:";
Check();
break;
case 2:
cout << "请输入需要查找的学生姓名:";
Check1();
break;
/*case 3:
cout << "请输入需要模糊查找的学生姓名:";*/
default:
cout << "选择错误" << endl;
break;
}
}
void Linklist::Modify()
{
cout << "请输入你须要修改的学生的学号:";//输入学号在check函数中输入即可,不用另外写了
//先查看需要修改的信息是否存在
if (Check())
{
//找到,重新录入即可
cout << "请输入修改后学生的学号:";
cin >> present->num;
cout << "请输入修改后学生的姓名:";
cin >> present->name;
cout << "请输入修改后学生的性别:";
cin >> present->sex;
cout << "请输入修改后学生的班级:";
cin >> present->classroom;
cout << "请输入修改后学生的高数成绩:";
cin >> present->hmath;
cout << "请输入修改后学生的英语成绩:";
cin >> present->eng;
present->sum = present->hmath + present->eng;
cout << "修改成功!!!" << endl;
}
//找不到Check函数会自动return false
else cout << "修改失败!" << endl;
}
void Linklist::Delete()
{//删除学生信息
cout << "请输入要删除的学生学号: ";
if (Check())
{
if (present->pre == NULL)
{
//头指针与当前指针指向同一个,将头指针给下一个
head = present->next;
delete present;
}
else if (present->pre != NULL)
{
//改变指针指向,前一个越过present 指向后一个
//不改变之前两个等号右边都是present
present->pre->next = present->next;
present->next->pre = present->pre;
delete present;
}
cout << "删除数据成功!" << endl;
}
else cout << "删除失败!" << endl;
}
void Linklist::Sort()
{
int a = 1, b = 1; present = head;
start:
while (present->next->next != NULL)
{
a = b;
//排序,小的放前面
//高数
if (present->hmath == present->next->hmath)
{
if (present->eng == present->next->eng)
{
//学号
if (present->num < present->next->num)
{//无需互换
present = present->next;
}
else if (present->num > present->next->num&&present->pre == NULL)
{
b++;
DX* p = present;
DX* q = p->next;
p->next = q->next;
q->next = p;
p->pre = q;
q->next = p;
q->pre = NULL;
}
//中间结点,尾结点不作考虑
else if (present->num > present->next->num && present->pre != NULL)
{
if (present->next->next != NULL)
{
b++;
DX* p = present;
DX* q = p->next;
p->pre->next = q;
q->pre = p->pre;
p->next = q->next;
q->next->pre = p;
p->pre = q;
q->next = p;
}
}
}
//英语
else if (present->eng < present->next->eng)
{//无需互换
present = present->next;
}
else if (present->eng > present->next->eng&&present->pre == NULL)
{
b++;
DX* p = present;
DX* q = p->next;
p->next = q->next;
q->next = p;
p->pre = q;
q->next = p;
q->pre = NULL;
}
//中间结点,尾结点不作考虑
else if (present->eng > present->next->eng && present->pre != NULL)
{
if (present->next->next != NULL)
{
b++;
DX* p = present;
DX* q = p->next;
p->pre->next = q;
q->pre = p->pre;
p->next = q->next;
q->next->pre = p;
p->pre = q;
q->next = p;
}
}
}
else if (present->hmath < present->next->hmath)
{//无需互换
present = present->next;
}
//头结点
else if (present->hmath > present->next->hmath&&present->pre == NULL)
{
b++;
DX* p = present;
DX* q = p->next;
p->next = q->next;
q->next = p;
p->pre = q;
q->next = p;
q->pre = NULL;
}
//中间结点,尾结点不作考虑
else if (present->hmath > present->next->hmath && present->pre != NULL)
{
if (present->next->next != NULL)
{
b++;
DX* p = present;
DX* q = p->next;
p->pre->next = q;
q->pre = p->pre;
p->next = q->next;
q->next->pre = p;
p->pre = q;
q->next = p;
}
}
while (a != b)
{//若一次完整循环下来没有顺序变化,退出
while (present->pre != NULL) {
present = present->pre;
} goto start;
}
}
//没有此次向前遍历,排序过后链表中只会留下最后一次插入的数据
while (present->pre != NULL) { present = present->pre; }
head = present;//因为头结点不为空,修改完后可能会被改变,此处从新定位头结点
//打印出排序好的链表
display();
do{
cout << setw(6) << present->num << setw(12) << present->name << setw(8) << present->sex
<< setw(8) << present->classroom << setw(8) << present->hmath << setw(8) << present->eng
<< setw(12) << present->sum << endl;
cout << " --------------------------------------------------------------------------" << endl;
present = present->next;//更新
} while (present->next != NULL);
}
//菜单
void menu()
{
cout << "-----------------------------------------------" << endl;
cout << "学生信息管理系统" << endl;
cout << "0.退出系统" << endl;
cout << "1.录入信息" << endl;
cout << "2.删除信息" << endl;
cout << "3.修改信息" << endl;
cout << "4.查找信息" << endl;
cout << "5.浏览信息" << endl;
cout << "6.排序" << endl;
cout << "-----------------------------------------------" << endl;
cout << "请输入(0-6):";
}
int main()
{
X.Creat();
//没有while循环switch语句只走一遍就结束了
while (1)
{
menu();
int input;
cin >> input;
switch (input)
{
case 0:
break;
case 1:
X.Input();
break;
case 2:
X.Delete();
break;
case 3:
X.Modify();
break;
case 4:
X.SelectCheck();
break;
case 5:
X.Output();
break;
case 6:
X.Sort();
break;
default:
cout << "输入错误!!!" << endl;
break;
}
//退出循环
if (input == 0)
{
break;//终止while
}
}
return 0;}
结果展现