• 基于C++实现考试报名系统


    1. 分析

    1.1 背景分析

    考试报名系统是一个学校不可缺少的部分,它对于学校的管理者和学生来说都至关重要,所以一个良好的考试报名系统应该能够为用户提供充足的信息和功能。考试报名系统对于学校加强考试管理有极其重要的作用。随着学生数量和考试数量的日益庞大,如何管理如此庞大的数据显得极为复杂,传统的手工管理工作量大且容易出错。

    随着计算机科学技术的不断成熟,使用计算机对考试报名系统进行管理,具有手工管理所无法比拟的优势。这些优点能够极大地提高学校和学生的效率,也是学校走向信息化、科学化、国际化的重要条件。因此,开发一套考试报名系统具有十分重要的意义。

    1.2 功能分析

    作为一个最简易的考试报名系统,首先应该有的功能就是输入同学们的考试报名情况并且可以予以显示。

    其次,考试报名系统还应该具有插入、删除、修改功能,以保证同学可以随时更改自己的考试报名情况。最后,考试报名系统软件还应该确保软件可以正常关闭,以及如果用户输入的数据有问题的时候可以做出简单的检查和提示。

    综上所述,一个考试报名系统至少应该具有输入、输出、插入、删除、修改、退出、健壮性的检测等功能。

    2. 设计

    2.1 数据结构设计

    如上功能分析所述,该系统要求频繁的插入、删除、修改操作,而链表进行插入、删除等操作十分简便,因此考虑使用链表数据结构。同时,为了实现简易,在第一个结点之前附加一个头结点,并用头指针指向头结点,使得增加或者删除头结点与处理其他结点方法相同,使得程序简洁,更方便单链表的前插法的设计,也不需要将第一个和最后一个结点做特殊处理。

    2.2 类结构设计

    经典的链表一般包括两个抽象数据类型(ADT)——链表结点类(NODE)与链表类(LinkList),而两个类之间的耦合关系可以采用嵌套、继承等多种关系。为方便处理,本系统将链表类(LinkList)声明为链表结点类(NODE)的友元,这样使得链表结点类(LinkList)可以访问链表结点。

    同时将考生的信息封装到 Stu 结构体中,使得结点类的定义更简洁,更方便插入、删除、修改等功能。

    2.3 成员与操作设计

    考生信息类(Stu)

    公有成员:

    int _ExamNum;		//准考证号
    string _name;		//姓名
    string _sex;		//性别
    int _age;		//年龄
    string _AppCate;	//报考类别
    
    • 1
    • 2
    • 3
    • 4
    • 5

    考生信息的构造函数:

    Stu() = default;								//默认构造函数
    Stu(const int ExamNum, const string &name, const string &sex,
        const int age, const string &AppCate);		//新考生信息的构造函数
    Stu::Stu(const int ExamNum, const string &name, const string &sex,
             const int age, const string &AppCate)
        :_ExamNum(ExamNum),_name(name),_sex(sex),_age(age),_AppCate(AppCate) {}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    链表结点类(NODE)

    私有成员:

    Stu data;		//封装考生信息于变量data
    NODE *next;	//指针域,用于指向下一结点信息
    
    • 1
    • 2

    友元:

    friend class LinkList;	//将LinkList类声明为NODE类的友元
    						//使得LinkList可以访问结点类的私有成员
    						//而其他地方不可以方位节点类,防止对数据的误处理
    
    • 1
    • 2
    • 3

    链表类(LinkList)

    私有成员:

    NODE * head = NULL;		//头指针, 指向头结点
    int _size = 0;			//链表中有效信息的数量
    
    • 1
    • 2

    公有操作:

    LinkList() = default;	//默认构造函数
    LinkList(int size);		//指定长度的链表构造函数
    ~LinkList();			//析构函数: 用于销毁链表中所有结点
    void InitLink();		//用于已经构建好的链表的数据填充
    void OutputLink();		//打印考生信息表
    void DestroyLink();		//从头结点开始销毁链表中所有结点
    bool FindLink(int num, Stu &buf);
    //寻找考生号为num的考生信息,如果找到储存到buf中, 否则返回false
    void InsertLink(int index, bool flag);
    //flag用于标识前插法和后插法, 在index位置的(前或后)插入考生信息
    bool DeleteLink(int num, Stu &buf);
    //删除考生号为num的考生信息, 如果存在该考生则储存到buf中
    void ChangeLink(int num);
    //修改考生号为num的考生信息, 否则输出相应的信息提示
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2.4 系统设计

    系统首先调用 StuSystem()函数实现对屏幕的初始化,完成对链表 list 的创建和输入数据工作,然后根据用户所输入的操作码(operatorCode)执行链表 list 对应的成员函数。

    3. 实现

    3.1 插入功能的实现

    3.1.1 插入功能流程图

    在这里插入图片描述

    3.1.2 插入功能核心代码

    用户输入插入位置部分

    while (index<0 || index>this->_size)
    //如果插入的位置非法, 则终止程序
    {
        cerr << "插入位置非法!\n";
        cerr << "请重新输入: ";
        cin >> index;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    初始化 move 结点部分

    NODE *move = (flag == BACK ? head->next : head);
    //后插法初始化为首结点; 前插法初始化为头结点,用move->next指向链表的每个结点
    
    • 1
    • 2

    插入部分

    for (int i=1; (flag == BACK ? move : move->next) != NULL; move = move->next,++i)
    	{
    		if (i == index)
    		{
    			NODE *fresh = new NODE;
    			if (!fresh)
    			{
    				cerr << "Memory Alloction Error!\n";
    				exit(-1);
    			}
    
    			cout << "请依次输入考生的考号, 姓名, 性别, 年龄及报考类型: " << endl;
    			cin >> fresh->data._ExamNum >> fresh->data._name >> fresh->data._sex
    				>> fresh->data._age >> fresh->data._AppCate;
    
    			//将新结点链到链表上
    			fresh->next = move->next;
    			move->next = fresh;
    
    			break;
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    3.1.3 插入功能截屏示例

    在这里插入图片描述
    在这里插入图片描述

    3.2 删除功能的实现

    3.2.1 删除功能流程图

    在这里插入图片描述

    3.2.2 删除功能核心代码

    if (!FindLink(num, buf))
    		//如果不存在该结点, 返回false
    	{
    		return false;
    	}
    
    	NODE *move = head;	
    		//因为需要获取删除结点的前一个结点, 所以move只能指向头结点
    	NODE *save;
    
    	for (; move->next != NULL; move = move->next)
    	{
    		if (move->next->data._ExamNum == num)
    		{
    			save = move->next;		//储存要删除的结点
    			buf = save->data;
    
    			move->next = move->next->next;
    				//将前一个结点链接到后一个结点上
    
    			//释放被删除的空间
    			delete save;
    			save = NULL;
    
    			break;
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    3.2.3 删除功能截屏示例

    在这里插入图片描述
    在这里插入图片描述

    3.3 查找功能的实现

    3.3.1 查找功能流程图

    在这里插入图片描述

    3.3.2 查找功能核心代码

    NODE *move = head->next;
    		//用move指向每一个链表的结点, 初始化为首结点
    	for (; move != NULL; move = move->next)
    	{
    		if (move->data._ExamNum == num)
    		{
    			buf = move->data;
    			return true;
    		}
    	}
    	return false;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    3.3.3 查找功能截图示例

    在这里插入图片描述

    在这里插入图片描述

    3.4 修改功能的实现

    3.4.1 修改功能流程图

    在这里插入图片描述

    3.4.2 修改功能核心代码

    Stu buf;
    	if (!FindLink(num, buf))
    		//如果不存在该结点, 则输出提示信息, 返回调用函数
    	{
    		printf("未找到该考生号!");
    		return;
    	}
    
    	NODE *move = head->next;
    	for (; move != NULL; move = move->next)
    	{
    		if (move->data._ExamNum == num)
    		{
    			cout << "请依次输入修改后的考号, 姓名, 性别, 年龄及报考类型: " << endl;
    			cin >> move->data._ExamNum >> move->data._name >> move->data._sex
    				>> move->data._age >> move->data._AppCate;
    			break;
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    3.4.3 修改功能截屏示例

    在这里插入图片描述

    在这里插入图片描述

    3.5 统计功能的实现

    3.5.1 统计功能流程图

    在这里插入图片描述

    3.5.2 统计功能核心代码

    NODE *move = head->next;
    if (this->_size == 0)
    	{
    		cout << "考生信息为空!\n";
    		return;
    	}
    
    	NODE *move = head->next;
    		//用move指向每一个链表的结点, 初始化为首结点
    	printf("\n考号   姓名   性别   年龄   报考类型\n");
    
    	for (; move != NULL; move = move->next)
    	{
    		printf("%-7d%-7s%-7s%-7d%-7s\n", move->data._ExamNum, move->data._name.c_str(),
    			move->data._sex.c_str(), move->data._age, move->data._AppCate.c_str());
    			//指定字段宽度打印信息
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    3.5.3 统计功能截屏示例

    在这里插入图片描述

    3.6 总体系统的实现

    3.6.1 总体系统流程图

    在这里插入图片描述

    3.6.2 总体系统核心代码

    //创建长度为size的链表
    	LinkList list(size);
    	//读入链表的结点信息
    	list.InitLink();
    	//打印初始的链表信息
    	list.OutputLink();
    
    	
    
    
    	while (operatorCode)
    	{
    		if (1 == operatorCode)	//插入
    		{
    			printf("你想前插还是后插?(后插0, 前插1): ");
    			cin >> flag;
    			printf("你想在第几个考生"); printf(flag == BACK ? "后插入: " : "前插入: ");
    			cin >> num;
    
    			list.InsertLink(num, flag);
    		}
    		else if (2 == operatorCode)	//删除
    		{
    			printf("请输入要删除的考生的考号: ");
    			cin >> num;
    			if (list.DeleteLink(num, buf))
    			{
    				printf("你删除的考生的信息是: ");
    				printf("%-7d%-7s%-7s%-7d%-7s\n", buf._ExamNum, buf._name.c_str(),
    					buf._sex.c_str(), buf._age, buf._AppCate.c_str());
    			}
    			else
    			{
    				printf("未找到该考生号!\n");
    			}
    		}
    		else if (3 == operatorCode)	//查找
    		{
    			printf("请输入你要查找的考生的考号: ");
    			cin >> num;
    			if (list.FindLink(num, buf))
    			{
    				printf("考号   姓名   性别   年龄   报考类型\n");
    				printf("%-7d%-7s%-7s%-7d%-7s\n", buf._ExamNum, buf._name.c_str(),
    					buf._sex.c_str(), buf._age, buf._AppCate.c_str());
    			}
    			else
    			{
    				printf("未找到该考号的考生!\n");
    			}
    		}
    		else if (4 == operatorCode)	//修改
    		{
    			printf("请输入你想修改信息的考生号: ");
    			cin >> num;
    			list.ChangeLink(num);
    		}
    		else if (5 == operatorCode)	//统计
    		{
    			list.OutputLink();
    		}
    		else  //非法操作码处理
    		{
    			cout << "操作非法!\n";
    			cout << "请重新输入\n";
    		}
    
    		printf("请输入您要进行的操作(1为插入, 2为删除, 3为查找, 4为修改, 5为统计, 0为取消操作)\n");
    		printf("请输入您要进行的操作: ");
    		cin >> operatorCode;
    	}
    	cout << "操作结束" << endl;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    3.6.3 总体系统截屏示例

    在这里插入图片描述

    4. 测试

    4.1 功能测试

    4.1.1 插入功能测试

    前插法测试用例:4 stu4 女 21 软件测试师

    预期结果:

    • stu1 女 20 软件设计师
    • stu4 女 21 软件测试师
    • stu2 男 21 软件开发师
    • stu3 男 20 软件设计师

    实验结果

    在这里插入图片描述

    后插法测试用例:5 stu5 女 19 软件开发师

    预期结果:

    • stu1 女 20 软件设计师
    • stu4 女 21 软件测试师
    • stu2 男 21 软件开发师
    • stu3 男 20 软件设计师
    • stu5 女 19 软件开发师

    实验结果

    在这里插入图片描述

    4.1.2 删除功能测试

    测试用例:删除考号为 4 的考生

    预期结果:

    • stu1 女 20 软件设计师
    • stu2 男 21 软件开发师
    • stu3 男 20 软件设计师
    • stu5 女 19 软件开发师

    实验结果:

    在这里插入图片描述

    4.1.3 查找功能测试

    测试用例:查找考号为 5 的考生

    预期结果:

    stu5 女 19 软件开发师

    实验结果:

    在这里插入图片描述

    4.1.4 修改功能测试

    测试用例:将考号 1 修改为性别女,年龄 20,报考种类移动开发员。

    预期结果:

    stu1 女 20 移动开发员

    实验结果:

    在这里插入图片描述

    4.1.5 统计功能测试

    测试用例:统计当前数据

    预期结果:

    • stu1 女 20 移动开发员
    • stu2 男 21 软件开发师
    • stu3 男 20 软件设计师
    • stu5 女 19 软件开发师

    实验结果:

    在这里插入图片描述

    4.2 边界测试

    4.2.1 初始化无输入数据

    测试用例:考生人数分别输入-1, 0, 3

    预期结果:给出错误提示,程序运行正常不崩溃,并提示用户重新输入。

    实验结果:

    在这里插入图片描述

    4.2.2 删除头结点

    测试用例:删除头结点

    预期结果:程序正常运行,不崩溃。

    实验结果:

    在这里插入图片描述

    4.2.3 删除后链表为空

    测试用例:删除前链表只有一个结点,删除后链表为空

    预期结果:程序正常运行,不崩溃。

    实验结果:

    在这里插入图片描述

    4.3 出错测试

    4.3.1 考生人数错误

    测试用例:输入考生人数为负数

    预期结果:程序给出提示信息,程序正常运行不崩溃,并让用户继续输入。

    实验结果:

    在这里插入图片描述

    4.3.2 操作码错误

    测试用例:输入操作码错误

    预期结果:程序给出提示信息,程序正常运行不崩溃,并允许用户重新输入。

    实验结果:

    在这里插入图片描述

    4.3.3 插入位置不存在

    测试用例:链表里有两条记录,向链表的第四个位置插入结点

    预期结果:程序给出错误信息,程序正常运行不崩溃,并允许用户重新输入。

    实验结果:

    在这里插入图片描述

    4.3.4 删除考号不存在

    测试用例:要删除的考号不存在

    预期结果:程序给出提示信息,程序正常运行不崩溃。

    实验结果:

    在这里插入图片描述

    4.3.5 查找考号不存在

    测试用例:要查找的考号不存在

    预期结果:程序给出错误信息,程序正常运行不崩溃。

    实验结果:

    在这里插入图片描述

  • 相关阅读:
    【Android安全】Frida 指定classloader | hook动态加载的类 | 安卓多apk hook
    【PAT(甲级)】1050 String Subtraction(用map<char,int>标记字符)
    Scala基础教程--16--泛型
    《动手学深度学习 Pytorch版》 5.3 延后初始化
    刷题笔记day01-数组
    java毕业生设计在线阅读系统计算机源码+系统+mysql+调试部署+lw
    Jenkins构建 Maven项目(微服务)并自动发布
    vue里面使用EventBus(事件总线)
    【Unity入门计划】用双血条方法控制伤害区域减血速度
    3 学习用特殊字符串联命令
  • 原文地址:https://blog.csdn.net/newlw/article/details/126360629