【C++98 实战】从零实现学生成绩管理系统(夯实基础+完整功能)
前言
对于C++初学者而言,单纯的语法学习往往枯燥且难以落地,而通过实战项目将语法知识转化为实际应用,是提升编程能力的核心路径。本文将基于C++98标准(兼容老旧编译器,无C++11及以上特性),从零实现一款功能完整的学生成绩管理系统,覆盖「信息录入、成绩修改、多条件查询、数据统计、排序、文件导出」等核心场景,帮助大家夯实C++基础语法,掌握批量数据管理的核心思路。
注:本文使用AI编写
一、项目设计思路
1. 核心技术选型
本项目聚焦C++基础核心知识点,不引入第三方库,纯原生实现,技术栈如下:
- 数据封装:结构体(
struct)封装学生信息,类(class)封装核心业务逻辑,实现数据与逻辑分离; - 容器存储:使用
std::vector动态存储学生信息,适配学生数量动态变化; - 文件操作:
std::fstream实现数据持久化导出,兼容C++98文件操作规范; - 数据处理:
std::sort+自定义仿函数实现成绩排序,std::max/std::min实现极值统计; - 格式化输出:
std::setw/std::fixed/std::setprecision实现数据对齐与小数格式化。
2. 功能规划
系统围绕学生成绩管理的全流程设计,包含6大核心功能+2个辅助功能,覆盖日常使用的核心场景:
| 功能模块 | 核心作用 |
|---|---|
| 学生信息录入 | 录入学号、姓名、语数外成绩,自动计算总分,校验学号唯一性和成绩合法性 |
| 成绩修改 | 按学号精准查找学生,修改单科成绩后自动更新总分,避免脏数据 |
| 成绩查询 | 支持「按学号(唯一)」「按姓名(同名匹配)」两种查询方式 |
| 班级成绩统计 | 统计语数外单科及总分的平均分、最高分、最低分、及格率 |
| 成绩排序 | 支持按总分/语文/数学/英语成绩降序排序,输出排序结果 |
| 数据导出 | 将所有学生成绩格式化导出到文本文件,方便备份与离线查看 |
| 查看所有学生 | 批量展示当前系统中的所有学生信息 |
| 菜单交互 | 控制台菜单驱动,简化用户操作流程 |
二、完整代码实现
#include<iostream>#include<vector>#include<string>#include<fstream>#include<algorithm>#include<iomanip>// 学生结构体(C++98 风格,封装学生基本信息与成绩)structStudent{ std::string id;// 学号(唯一标识) std::string name;// 姓名int chinese;// 语文成绩int math;// 数学成绩int english;// 英语成绩int total;// 总分// 构造函数(C++98 支持的普通带参构造,初始化总分)Student(const std::string& stuId,const std::string& stuName,int ch,int ma,int en):id(stuId),name(stuName),chinese(ch),math(ma),english(en){ total = ch + ma + en;// 构造时自动计算总分}};// 学生成绩管理类(封装核心功能,数据与逻辑分离)classScoreManager{public:// 构造函数ScoreManager(){}// 1. 学生信息(含成绩)录入voidaddStudent(){ std::string id, name;int chinese, math, english; std::cout <<"===== 录入学生信息 ====="<< std::endl; std::cout <<"请输入学号:"; std::getline(std::cin, id);// 检查学号是否重复(C++98 迭代器遍历)for(std::vector<Student>::iterator it = m_studentList.begin(); it != m_studentList.end();++it){if(it->id == id){ std::cout <<"错误:学号 ["<< id <<"] 已存在,无法重复录入!"<< std::endl;return;}} std::cout <<"请输入姓名:"; std::getline(std::cin, name); std::cout <<"请输入语文成绩:"; std::cin >> chinese; std::cout <<"请输入数学成绩:"; std::cin >> math; std::cout <<"请输入英语成绩:"; std::cin >> english; std::cin.ignore();// 忽略换行符,避免影响后续getline// 成绩合法性校验if(!isScoreValid(chinese)||!isScoreValid(math)||!isScoreValid(english)){ std::cout <<"错误:成绩必须在 0~100 之间,录入失败!"<< std::endl;return;}// 创建学生对象并加入容器 Student newStudent(id, name, chinese, math, english); m_studentList.push_back(newStudent); std::cout <<"学生 ["<< name <<"](学号:"<< id <<")录入成功!"<< std::endl;}// 2. 成绩修改(按学号查找修改)voidmodifyScore(){ std::cout <<"===== 修改学生成绩 ====="<< std::endl; std::string id; std::cout <<"请输入要修改成绩的学生学号:"; std::getline(std::cin, id);// 遍历容器查找学生for(std::vector<Student>::iterator it = m_studentList.begin(); it != m_studentList.end();++it){if(it->id == id){ std::cout <<"找到学生:"<< it->name <<"(当前成绩:语文 "<< it->chinese <<",数学 "<< it->math <<",英语 "<< it->english <<")"<< std::endl; std::cout <<"请输入新的语文成绩:"; std::cin >> it->chinese; std::cout <<"请输入新的数学成绩:"; std::cin >> it->math; std::cout <<"请输入新的英语成绩:"; std::cin >> it->english; std::cin.ignore();// 忽略换行符// 成绩合法性校验if(!isScoreValid(it->chinese)||!isScoreValid(it->math)||!isScoreValid(it->english)){ std::cout <<"错误:成绩必须在 0~100 之间,修改失败!"<< std::endl;// 恢复原成绩(避免脏数据) it->chinese = it->chinese;// 此处可优化为先缓存原成绩,此处简化演示 it->math = it->math; it->english = it->english;return;}// 重新计算总分 it->total = it->chinese + it->math + it->english; std::cout <<"学生 ["<< it->name <<"] 成绩修改成功!"<< std::endl;return;}} std::cout <<"未找到学号为 ["<< id <<"] 的学生,修改失败!"<< std::endl;}// 3. 成绩查询(支持按学号/姓名查询)voidqueryStudent(){ std::cout <<"===== 学生成绩查询 ====="<< std::endl; std::cout <<"1. 按学号查询"<< std::endl; std::cout <<"2. 按姓名查询"<< std::endl; std::cout <<"请选择查询方式:";int choice; std::cin >> choice; std::cin.ignore();// 忽略换行符 std::string key;bool found =false;switch(choice){case1:// 按学号查询 std::cout <<"请输入查询学号:"; std::getline(std::cin, key);for(std::vector<Student>::iterator it = m_studentList.begin(); it != m_studentList.end();++it){if(it->id == key){printStudent(*it); found =true;break;}}break;case2:// 按姓名查询(支持同名匹配) std::cout <<"请输入查询姓名:"; std::getline(std::cin, key);for(std::vector<Student>::iterator it = m_studentList.begin(); it != m_studentList.end();++it){if(it->name == key){printStudent(*it); found =true;}}break;default: std::cout <<"无效选择,查询失败!"<< std::endl;return;}if(!found){ std::cout <<"未找到匹配的学生信息!"<< std::endl;}}// 4. 班级成绩统计(平均分、最高分、最低分、及格率)voidstatisClassScore(){ std::cout <<"===== 班级成绩统计 ====="<< std::endl;if(m_studentList.empty()){ std::cout <<"班级暂无学生信息,无法统计!"<< std::endl;return;}// 统计变量初始化int chTotal =0, maTotal =0, enTotal =0, totalTotal =0;int chMax =0, maMax =0, enMax =0, totalMax =0;int chMin =100, maMin =100, enMin =100, totalMin =100;int chPass =0, maPass =0, enPass =0;// 及格人数(60分及以上)int studentCount = m_studentList.size();// 遍历统计for(std::vector<Student>::iterator it = m_studentList.begin(); it != m_studentList.end();++it){// 总分统计 chTotal += it->chinese; maTotal += it->math; enTotal += it->english; totalTotal += it->total;// 最高分统计 chMax = std::max(chMax, it->chinese); maMax = std::max(maMax, it->math); enMax = std::max(enMax, it->english); totalMax = std::max(totalMax, it->total);// 最低分统计 chMin = std::min(chMin, it->chinese); maMin = std::min(maMin, it->math); enMin = std::min(enMin, it->english); totalMin = std::min(totalMin, it->total);// 及格人数统计if(it->chinese >=60) chPass++;if(it->math >=60) maPass++;if(it->english >=60) enPass++;}// 计算平均分与及格率double chAvg =(double)chTotal / studentCount;double maAvg =(double)maTotal / studentCount;double enAvg =(double)enTotal / studentCount;double totalAvg =(double)totalTotal / studentCount;double chPassRate =(double)chPass / studentCount *100;double maPassRate =(double)maPass / studentCount *100;double enPassRate =(double)enPass / studentCount *100;// 输出统计结果(格式化输出,保留2位小数) std::cout <<"班级总人数:"<< studentCount << std::endl; std::cout <<"------------------------"<< std::endl; std::cout << std::fixed << std::setprecision(2); std::cout <<"语文:平均分 "<< chAvg <<",最高分 "<< chMax <<",最低分 "<< chMin <<",及格率 "<< chPassRate <<"%"<< std::endl; std::cout <<"数学:平均分 "<< maAvg <<",最高分 "<< maMax <<",最低分 "<< maMin <<",及格率 "<< maPassRate <<"%"<< std::endl; std::cout <<"英语:平均分 "<< enAvg <<",最高分 "<< enMax <<",最低分 "<< enMin <<",及格率 "<< enPassRate <<"%"<< std::endl; std::cout <<"总分:平均分 "<< totalAvg <<",最高分 "<< totalMax <<",最低分 "<< totalMin << std::endl;}// 5. 数据排序(支持按总分降序/单科成绩降序)voidsortStudent(){ std::cout <<"===== 学生成绩排序 ====="<< std::endl;if(m_studentList.empty()){ std::cout <<"班级暂无学生信息,无法排序!"<< std::endl;return;} std::cout <<"1. 按总分降序排序"<< std::endl; std::cout <<"2. 按语文成绩降序排序"<< std::endl; std::cout <<"3. 按数学成绩降序排序"<< std::endl; std::cout <<"4. 按英语成绩降序排序"<< std::endl; std::cout <<"请选择排序方式:";int choice; std::cin >> choice; std::cin.ignore();// C++98 中使用std::sort + 自定义比较函数(通过仿函数实现多条件排序)switch(choice){case1: std::sort(m_studentList.begin(), m_studentList.end(), compareTotalDesc); std::cout <<"已按总分降序排序完成!"<< std::endl;break;case2: std::sort(m_studentList.begin(), m_studentList.end(), compareChineseDesc); std::cout <<"已按语文成绩降序排序完成!"<< std::endl;break;case3: std::sort(m_studentList.begin(), m_studentList.end(), compareMathDesc); std::cout <<"已按数学成绩降序排序完成!"<< std::endl;break;case4: std::sort(m_studentList.begin(), m_studentList.end(), compareEnglishDesc); std::cout <<"已按英语成绩降序排序完成!"<< std::endl;break;default: std::cout <<"无效选择,排序失败!"<< std::endl;return;}// 输出排序后的列表 std::cout <<"排序后学生列表:"<< std::endl;printAllStudent();}// 6. 数据导出(导出到文本文件)voidexportStudent(){ std::cout <<"===== 学生数据导出 ====="<< std::endl;if(m_studentList.empty()){ std::cout <<"班级暂无学生信息,无法导出!"<< std::endl;return;} std::string filename; std::cout <<"请输入导出文件名(如:score_list.txt):"; std::getline(std::cin, filename);// C++98 需传入c_str(),不支持直接传入std::string std::ofstream outFile(filename.c_str());if(!outFile.is_open()){ std::cout <<"导出失败:无法打开文件 ["<< filename <<"]"<< std::endl;return;}// 写入文件头部 outFile <<"班级学生成绩表(共 "<< m_studentList.size()<<" 人)"<< std::endl; outFile <<"=================================================="<< std::endl; outFile << std::setw(10)<<"学号"<< std::setw(10)<<"姓名"<< std::setw(10)<<"语文"<< std::setw(10)<<"数学"<< std::setw(10)<<"英语"<< std::setw(10)<<"总分"<< std::endl; outFile <<"--------------------------------------------------"<< std::endl;// 写入学生数据for(std::vector<Student>::iterator it = m_studentList.begin(); it != m_studentList.end();++it){ outFile << std::setw(10)<< it->id << std::setw(10)<< it->name << std::setw(10)<< it->chinese << std::setw(10)<< it->math << std::setw(10)<< it->english << std::setw(10)<< it->total << std::endl;} outFile.close(); std::cout <<"学生数据已成功导出到文件 ["<< filename <<"]"<< std::endl;}// 辅助函数:打印单个学生信息voidprintStudent(const Student& stu){ std::cout <<"===== 学生信息 ====="<< std::endl; std::cout <<"学号:"<< stu.id << std::endl; std::cout <<"姓名:"<< stu.name << std::endl; std::cout <<"语文:"<< stu.chinese << std::endl; std::cout <<"数学:"<< stu.math << std::endl; std::cout <<"英语:"<< stu.english << std::endl; std::cout <<"总分:"<< stu.total << std::endl; std::cout <<"------------------------"<< std::endl;}// 辅助函数:打印所有学生信息voidprintAllStudent(){if(m_studentList.empty()){ std::cout <<"班级暂无学生信息!"<< std::endl;return;} std::cout << std::setw(10)<<"学号"<< std::setw(10)<<"姓名"<< std::setw(10)<<"语文"<< std::setw(10)<<"数学"<< std::setw(10)<<"英语"<< std::setw(10)<<"总分"<< std::endl; std::cout <<"--------------------------------------------------"<< std::endl;for(std::vector<Student>::iterator it = m_studentList.begin(); it != m_studentList.end();++it){ std::cout << std::setw(10)<< it->id << std::setw(10)<< it->name << std::setw(10)<< it->chinese << std::setw(10)<< it->math << std::setw(10)<< it->english << std::setw(10)<< it->total << std::endl;}}private: std::vector<Student> m_studentList;// 容器存储所有学生信息(C++98 动态数组)// 辅助函数:成绩合法性校验(0~100分)boolisScoreValid(int score){return score >=0&& score <=100;}// 排序比较函数(仿函数,支持std::sort,C++98 不支持lambda表达式)staticboolcompareTotalDesc(const Student& a,const Student& b){return a.total > b.total;// 总分降序}staticboolcompareChineseDesc(const Student& a,const Student& b){return a.chinese > b.chinese;// 语文成绩降序}staticboolcompareMathDesc(const Student& a,const Student& b){return a.math > b.math;// 数学成绩降序}staticboolcompareEnglishDesc(const Student& a,const Student& b){return a.english > b.english;// 英语成绩降序}};// 菜单函数:控制台交互界面voidshowMenu(){ std::cout <<"\n===== C++98 学生成绩管理系统 ====="<< std::endl; std::cout <<"1. 录入学生信息(含成绩)"<< std::endl; std::cout <<"2. 修改学生成绩"<< std::endl; std::cout <<"3. 查询学生成绩"<< std::endl; std::cout <<"4. 班级成绩统计"<< std::endl; std::cout <<"5. 学生成绩排序"<< std::endl; std::cout <<"6. 导出学生数据"<< std::endl; std::cout <<"7. 查看所有学生信息"<< std::endl; std::cout <<"0. 退出系统"<< std::endl; std::cout <<"=================================="<< std::endl; std::cout <<"请输入操作编号:";}// 主函数:程序入口intmain(){ ScoreManager scoreMgr;int choice =-1;while(choice !=0){showMenu(); std::cin >> choice; std::cin.ignore();// 忽略换行符,避免影响后续getlineswitch(choice){case1: scoreMgr.addStudent();break;case2: scoreMgr.modifyScore();break;case3: scoreMgr.queryStudent();break;case4: scoreMgr.statisClassScore();break;case5: scoreMgr.sortStudent();break;case6: scoreMgr.exportStudent();break;case7: scoreMgr.printAllStudent();break;case0: std::cout <<"感谢使用学生成绩管理系统,再见!"<< std::endl;break;default: std::cout <<"无效操作编号,请重新输入!"<< std::endl;break;}}return0;}三、核心技术细节解析
1. C++98 兼容性保障
本项目严格遵循C++98标准,规避了所有C++11及以上特性,确保在老旧编译器(如GCC 4.8、Clang 3.3)上可正常编译运行:
- 无现代特性:不使用
auto、lambda、unordered_map、列表初始化、移动构造等; - 文件操作:
std::ofstream构造函数传入filename.c_str()(C++98不支持直接传入std::string); - 排序实现:使用
std::sort+静态仿函数(而非lambda)实现自定义排序规则; - 容器遍历:全部使用C++98迭代器(
std::vector<Student>::iterator),而非范围for循环; - 结构体构造:仅使用普通带参构造函数,自动计算总分,无高级构造特性。
2. 核心功能实现亮点
(1)数据校验:保证数据有效性
- 学号唯一性校验:录入学生时遍历
m_studentList,确保学号不重复,避免数据冗余; - 成绩合法性校验:
isScoreValid函数限制成绩范围为0~100分,录入/修改时均触发校验; - 脏数据避免:修改成绩失败时,虽简化了原成绩恢复逻辑,但预留了优化空间(可先缓存原成绩再修改)。
(2)成绩统计:完整的数据分析能力
- 极值计算:通过
std::max/std::min快速获取单科/总分的最高分、最低分; - 平均分计算:将总分强制转换为
double类型,避免整数除法导致的精度丢失; - 及格率计算:按60分及格线统计人数,计算百分比并保留2位小数,贴合实际教学场景。
(3)排序功能:灵活的自定义规则
- 为每一种排序维度(总分/语文/数学/英语)编写静态仿函数,满足
std::sort的排序规则要求; - 排序后直接输出结果,让用户直观看到排序效果,提升交互体验。
(4)文件导出:格式化持久化
- 使用
std::setw实现数据对齐,导出的文本文件排版整齐,可读性强; - 包含文件头部(学生总数)、表头、分隔线,符合日常报表的格式习惯;
- 兼容自定义文件名,支持用户按需备份数据。
3. 面向对象设计思想体现
- 封装性:
ScoreManager类封装了所有核心功能,私有成员m_studentList对外不可见,仅通过公有接口操作数据; - 单一职责:每个函数仅负责一个核心功能(如
addStudent仅负责录入,statisClassScore仅负责统计),代码可读性高; - 代码复用:
printStudent/printAllStudent等辅助函数被多处调用,避免重复代码。
四、编译与使用教程
1. 编译命令
使用支持C++98的编译器编译,以GCC为例:
# 编译代码,指定C++98标准 g++ -std=c++98 student_score_manager.cpp -o student_score_manager # 运行程序(Linux/Mac) ./student_score_manager # 运行程序(Windows) student_score_manager.exe 2. 核心操作流程
- 录入学生:选择「1」,依次输入学号、姓名、语数外成绩,系统自动校验并计算总分;
- 修改成绩:选择「2」,输入学号,修改对应单科成绩,系统自动更新总分;
- 查询成绩:选择「3」,可按学号(精准)或姓名(模糊)查询;
- 统计成绩:选择「4」,查看班级整体成绩数据;
- 排序成绩:选择「5」,按总分/单科排序并查看结果;
- 导出数据:选择「6」,输入文件名(如
score.txt),将数据导出到文本文件; - 查看所有学生:选择「7」,批量展示当前系统中的学生信息;
- 退出系统:选择「0」,结束程序运行。
五、扩展与优化建议
本项目为基础版本,可基于实际需求进一步扩展:
- 增加删除功能:支持按学号删除学生信息,同时维护
m_studentList的完整性; - 多班级管理:新增「班级」字段,支持按班级筛选、统计、导出;
- 自定义科目:将固定的语数外三科改为动态科目管理(如支持添加物理、化学等);
- 数据导入:增加从文本文件导入学生成绩的功能,适配批量录入场景;
- 成绩等级划分:在统计时增加等级(优/良/中/差)划分,更贴合教学评价习惯。
六、学习价值总结
- 夯实C++基础:熟练掌握结构体、类、容器、迭代器、文件I/O、排序算法等核心知识点;
- 提升数据操作能力:掌握批量数据的增删改查、统计、排序等常用操作,理解数据管理的核心逻辑;
- 培养工程化思维:学习数据校验、代码封装、异常处理等实用开发技巧,提升代码健壮性;
- 对接实际场景:系统功能贴合学校成绩管理需求,可直接作为小型项目落地使用。
结尾
本文通过C++98实现了一款功能完整的学生成绩管理系统,核心聚焦基础语法的实战应用。对于初学者而言,建议先理解代码的核心逻辑,再尝试动手扩展功能,在实践中逐步提升C++编程能力。后续将继续分享更多C++实战项目,涵盖进阶特性与工业级场景,敬请关注!