前言
对于 C++ 初学者而言,单纯的语法学习往往枯燥且难以落地。通过实战项目将语法知识转化为实际应用,是提升编程能力的核心路径。本文将基于 C++98 标准(兼容老旧编译器,无 C++11 及以上特性),从零实现一款功能完整的学生成绩管理系统。
系统覆盖信息录入、成绩修改、多条件查询、数据统计、排序及文件导出等核心场景,帮助大家夯实基础语法,掌握批量数据管理的思路。
一、项目设计思路
1. 核心技术选型
本项目聚焦 C++ 基础核心知识点,不引入第三方库,纯原生实现:
- 数据封装:结构体(
struct)封装学生信息,类(class)封装核心业务逻辑,实现数据与逻辑分离; - 容器存储:使用
std::vector动态存储学生信息,适配数量变化; - 文件操作:
std::fstream实现数据持久化,兼容 C++98 规范; - 数据处理:
std::sort+ 自定义仿函数实现排序,std::max/min统计极值; - 格式化输出:
std::setw/fixed/setprecision对齐数据。
2. 功能规划
系统围绕全流程设计,包含 6 大核心功能 +2 个辅助功能:
| 功能模块 | 核心作用 |
|---|---|
| 学生信息录入 | 录入学号、姓名、语数外成绩,自动计算总分,校验唯一性和合法性 |
| 成绩修改 | 按学号查找,修改单科后自动更新总分,避免脏数据 |
| 成绩查询 | 支持「按学号」和「按姓名」两种查询方式 |
| 班级成绩统计 | 统计平均分、最高分、最低分、及格率 |
| 成绩排序 | 支持按总分/单科降序排序 |
| 数据导出 | 格式化导出到文本文件,方便备份 |
| 查看所有学生 | 批量展示当前系统中的所有学生信息 |
| 菜单交互 | 控制台菜单驱动,简化操作流程 |
二、完整代码实现
为了保持 C++98 的兼容性,我们严格规避了 auto、lambda 表达式等现代特性。下面是完整的工程代码,包含了结构体定义、管理类封装以及主程序入口。
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <algorithm>
#include <iomanip>
// 学生结构体(C++98 风格,封装学生基本信息与成绩)
struct Student {
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;
}
};
// 学生成绩管理类(封装核心功能,数据与逻辑分离)
class ScoreManager {
public:
// 构造函数
ScoreManager() {}
// 1. 学生信息(含成绩)录入
void addStudent() {
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. 成绩修改(按学号查找修改)
void modifyScore() {
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;
// 恢复原成绩(此处简化演示,实际可先缓存)
return;
}
// 重新计算总分
it->total = it->chinese + it->math + it->english;
std::cout << "学生 [" << it->name << "] 成绩修改成功!" << std::endl;
return;
}
}
std::cout << "未找到学号为 [" << id << "] 的学生,修改失败!" << std::endl;
}
// 3. 成绩查询(支持按学号/姓名查询)
void queryStudent() {
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) {
case 1: // 按学号查询
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;
case 2: // 按姓名查询(支持同名匹配)
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. 班级成绩统计(平均分、最高分、最低分、及格率)
void statisClassScore() {
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. 数据排序(支持按总分降序/单科成绩降序)
void sortStudent() {
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) {
case 1:
std::sort(m_studentList.begin(), m_studentList.end(), compareTotalDesc);
std::cout << "已按总分降序排序完成!" << std::endl;
break;
case 2:
std::sort(m_studentList.begin(), m_studentList.end(), compareChineseDesc);
std::cout << "已按语文成绩降序排序完成!" << std::endl;
break;
case 3:
std::sort(m_studentList.begin(), m_studentList.end(), compareMathDesc);
std::cout << "已按数学成绩降序排序完成!" << std::endl;
break;
case 4:
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. 数据导出(导出到文本文件)
void exportStudent() {
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;
}
// 辅助函数:打印单个学生信息
void printStudent(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;
}
// 辅助函数:打印所有学生信息
void printAllStudent() {
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 分)
bool isScoreValid(int score) {
return score >= 0 && score <= 100;
}
// 排序比较函数(仿函数,支持 std::sort,C++98 不支持 lambda 表达式)
static bool compareTotalDesc(const Student& a, const Student& b) {
return a.total > b.total; // 总分降序
}
static bool compareChineseDesc(const Student& a, const Student& b) {
return a.chinese > b.chinese; // 语文成绩降序
}
static bool compareMathDesc(const Student& a, const Student& b) {
return a.math > b.math; // 数学成绩降序
}
static bool compareEnglishDesc(const Student& a, const Student& b) {
return a.english > b.english; // 英语成绩降序
}
};
// 菜单函数:控制台交互界面
void showMenu() {
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 << "请输入操作编号:";
}
// 主函数:程序入口
int main() {
ScoreManager scoreMgr;
int choice = -1;
while (choice != 0) {
showMenu();
std::cin >> choice;
std::cin.ignore(); // 忽略换行符,避免影响后续 getline
switch (choice) {
case 1:
scoreMgr.addStudent();
break;
case 2:
scoreMgr.modifyScore();
break;
case 3:
scoreMgr.queryStudent();
break;
case 4:
scoreMgr.statisClassScore();
break;
case 5:
scoreMgr.sortStudent();
break;
case 6:
scoreMgr.exportStudent();
break;
case 7:
scoreMgr.printAllStudent();
break;
case 0:
std::cout << "感谢使用学生成绩管理系统,再见!" << std::endl;
break;
default:
std::cout << "无效操作编号,请重新输入!" << std::endl;
break;
}
}
return 0;
}
三、核心技术细节解析
1. C++98 兼容性保障
本项目严格遵循 C++98 标准,确保在老旧编译器(如 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/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++98 实现这款学生成绩管理系统,核心在于基础语法的实战应用。对于初学者而言,建议先理解代码的核心逻辑,再尝试动手扩展功能,在实践中逐步提升 C++ 编程能力。后续将继续分享更多 C++ 实战项目,涵盖进阶特性与工业级场景。

