【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)上可正常编译运行:

  • 无现代特性:不使用autolambdaunordered_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. 录入学生:选择「1」,依次输入学号、姓名、语数外成绩,系统自动校验并计算总分;
  2. 修改成绩:选择「2」,输入学号,修改对应单科成绩,系统自动更新总分;
  3. 查询成绩:选择「3」,可按学号(精准)或姓名(模糊)查询;
  4. 统计成绩:选择「4」,查看班级整体成绩数据;
  5. 排序成绩:选择「5」,按总分/单科排序并查看结果;
  6. 导出数据:选择「6」,输入文件名(如score.txt),将数据导出到文本文件;
  7. 查看所有学生:选择「7」,批量展示当前系统中的学生信息;
  8. 退出系统:选择「0」,结束程序运行。

五、扩展与优化建议

本项目为基础版本,可基于实际需求进一步扩展:

  1. 增加删除功能:支持按学号删除学生信息,同时维护m_studentList的完整性;
  2. 多班级管理:新增「班级」字段,支持按班级筛选、统计、导出;
  3. 自定义科目:将固定的语数外三科改为动态科目管理(如支持添加物理、化学等);
  4. 数据导入:增加从文本文件导入学生成绩的功能,适配批量录入场景;
  5. 成绩等级划分:在统计时增加等级(优/良/中/差)划分,更贴合教学评价习惯。

六、学习价值总结

  1. 夯实C++基础:熟练掌握结构体、类、容器、迭代器、文件I/O、排序算法等核心知识点;
  2. 提升数据操作能力:掌握批量数据的增删改查、统计、排序等常用操作,理解数据管理的核心逻辑;
  3. 培养工程化思维:学习数据校验、代码封装、异常处理等实用开发技巧,提升代码健壮性;
  4. 对接实际场景:系统功能贴合学校成绩管理需求,可直接作为小型项目落地使用。

结尾

本文通过C++98实现了一款功能完整的学生成绩管理系统,核心聚焦基础语法的实战应用。对于初学者而言,建议先理解代码的核心逻辑,再尝试动手扩展功能,在实践中逐步提升C++编程能力。后续将继续分享更多C++实战项目,涵盖进阶特性与工业级场景,敬请关注!

Read more

Python:AI开发第一语言的全面剖析

Python:AI开发第一语言的全面剖析

文章目录 * 引言 * 1. Python的历史与AI开发的契合 * 1.1 Python的诞生与设计哲学 * 1.2 Python与AI发展的历史交汇 * 2. 语言特性如何支持AI开发 * 2.1 动态类型与交互式编程 * 2.2 简洁优雅的语法 * 2.3 高级数据结构的原生支持 * 2.4 函数式编程特性 * 2.5 强大的元编程能力 * 3. 丰富的AI生态系统和库支持 * 3.1 深度学习框架 * TensorFlow * PyTorch * JAX * 3.2 传统机器学习库 * Scikit-learn * XGBoost、LightGBM和CatBoost * 3.3 数据处理和可视化库 * Pandas * NumPy和SciPy * Matplotlib和Seaborn * 3.4 自然语言处理库

By Ne0inhk

C++:使用传输和交换实现聚类算法(附带源码)

一、项目背景详细介绍 在前面的聚类算法实现中,我们已经系统地介绍并实现了 K-Means 聚类算法。K-Means 以其简单高效著称,但在真实工程与统计建模中,它存在一些先天局限性: * 对异常值(Outliers)极其敏感 * 必须使用“均值”作为中心(不一定是实际样本) * 仅适用于欧氏空间 * 对非凸分布表现较差 在许多实际应用场景中,我们更希望: 聚类中心一定是“真实样本点”,并且对异常值更鲁棒。 这正是 K-Medoids 聚类算法(也称 PAM,Partitioning Around Medoids)的出发点。 1.1 什么是“传输”和“交换”思想? K-Medoids 并不是通过“求均值”来更新中心,而是通过: * 传输(Relocation): 将样本重新分配到最近的中心(medoid) * 交换(

By Ne0inhk

StarUML(6.3.3)2025-10-24更新!下载、破解、汉化及搭建C++扩展,从0到1全攻略教程(Windows11)

-1#主包作为第一次配StarUML环境可谓是吃进苦头,像无头苍蝇般,这里无偿分享给大家,如何从0到1实现汉化、破解、及解决软件c++扩展下载失败的问题 1.StartUML的下载 1.1官网网址: StarUMLhttps://staruml.io/ 1.2进去后按照此: 1.3然后点击运行,其正常界面如(代表下载成功): 2.StartUML的汉化及破解 2.1找到StartUML的安装目录(如1.2可知,一般在C盘的Program Files里) 在其根目录下找到 resources(如图): 2.2进入resources文件夹,找到 app.asar: 2.3 访问此网址: https://github.com/X1a0He/StarUML-CrackedAndTranslatehttps://github.com/X1a0He/StarUML-CrackedAndTranslate  进去之后点击

By Ne0inhk