原项目介绍与分析
项目介绍
这是一个学生成绩管理系统,该系统在其开发者手中时原计划是要实现包括增加、删除、修改学生和研究生的成绩并进行一个统一管理,但因为时间原因匆匆了事,开发者仅仅实现了其中的增和删减功能,以及完成了一个保存数据的半成品,据原开发者所说是在优化代码的时候可能把关键代码优化掉了,但没有精力再次复查,便只残存了一个办成品,该项目现被交接到了我的手上进行逆向软件设计和开发的练手。
项目来源
在大一上学期我室友练手用的一个小项目,但其中功能较少,且创新性不足,对于使用者来说非常不友好,不仅如此,程序在使用过后因为某些原因无法保存到电脑文件,导致每次打开程序都需要重新输入成绩,非常不便,但该程序又承载者这位朋友的一些青春回忆,所以希望我能够将其进一步完善。
原先项目分析
根据我与原开发者的沟通,我画出了他心目中该项目应该有的样子(图1-1)
图1-1
但可惜的是,他所完成的不过是其中的一半都不到,仅仅有包含GStudent、Student、removeGStudent、removeStudent的四个类,以及半成品的loadFromFile和saveToFile这五个类,当时他所偷的懒可见一斑,当时他的系统运行起来是这样的(图1-2)
图1-2
没有加载和保存数据选项是因为这串代码的存在会让系统出现编译错误,甚至无法运行,所以将其暂时搁置,等待后期进行重新添加。
在正常情况下,该项目运行时是这样的(图1-3)
非常的简单,简单到不会出现任何错误,让人放心,但也让使用者非常难受,这更坚定了我进行修改的决心。
项目改进
1.对于该项目功能的增加设计
首先,原先系统的内容非常空洞和单薄,功能单一,完全无法满足作为学生成绩管理系统的需求,所以在项目的逆向开发中,我重新设计了该项目的构架和功能设定,并为此增加了修改学生成绩的功能,将学生总分进行排序,并增加了显示所有学生信息的操作,完美实现了开发者原先想这个系统成为的样子(图1-1)在系统运行后,菜单页面变成了如图(图2-1)
接下来我将分别介绍其中的改进
1.1 修改学生
// 修改学生信息
void Class::modifyStudent(int studentNumber, const Student& newStudent) {
for (auto& student : students) {
if (student.studentNumber == studentNumber) {
student = newStudent;
break;
}
}
}
1.2 修改研究生
// 修改研究生信息
void Class::modifyGStudent(int studentNumber, const GStudent& newGStudent) {
for (auto& gstudent : gstudents) {
if (gstudent.studentNumber == studentNumber) {
gstudent = newGStudent;
break;
}
}
}
1.3 按总分排序学生并显示
// 学生按总成绩排序,从高到低
void Class::sortStudents() {
sort(students.begin(), students.end(),
[](const Student& a, const Student& b) { return a.getTotalScore() > b.getTotalScore(); });
sort(gstudents.begin(), gstudents.end(),
[](const GStudent& a, const GStudent& b) { return a.getTotalScore() > b.getTotalScore(); });
}
1.4显示所有学生
// 显示学生列表
void Class::display() {
for (const auto& student : students) {
cout << student << endl;
}
for (const auto& gstudent : gstudents) {
cout << gstudent << endl;
}
}
2.对于该项目功能的修正设计:
在这个项目拿到手的时候,该系统无法正常将数据保存到文件,以及从文件加载数据,在仔细阅读了相对应的代码后,我找到了代码其中出现的逻辑错误,并进行改进,具体过程如下:
原功能出错分析:
原代码:
// 从文件加载数据
void Class::loadFromFile( string filename ) {
ifstream infile(filename);
if (infile.is_open()) {
students.clear();
gstudents.clear();
string type;
while (infile >> type) {
if (type == "S") {
string name;
int studentNumber;
infile >> name >> studentNumber;
Student student(name, studentNumber);
int subjectCount;
infile >> subjectCount;
for (int i = 0; i < subjectCount; ++i) {
string subject;
float score;
infile >> subject >> score;
student.addScore(Score(subject, score));
}
students.push_back(student);
}
else if (type == "G") {
string name;
int studentNumber;
string projectName;
infile >> name >> studentNumber >> projectName;
GStudent gstudent(name, studentNumber, projectName);
int subjectCount;
infile >> subjectCount;
for (int i = 0; i < subjectCount; ++i) {
string subject;
float score;
infile >> subject >> score;
gstudent.addScore(Score(subject, score));
}
gstudents.push_back(gstudent);
}
}
infile.close();
}
else {
cout << "打开文件错误。" << endl;
}
}
在我看来,他的代码逻辑上基本正确,并无大碍,但其中如果文件的类型不符合预期,程序可能会崩溃,并且在调用 students.clear()
和 gstudents.clear()
之前,如果容器中已经存在数据,可能会导致内存泄漏或不必要的资源占用,这些都是需要改进的点,且他的代码有一些冗余之处,所以我对代码进行了修改如下:
// 从文件加载数据
void Class::loadFromFile(const string& filename) {
ifstream infile(filename);
if (!infile.is_open()) {
cout << "打开文件错误: " << filename << endl;
return;
}
// 清空现有数据
students.clear();
gstudents.clear();
string type;
while (infile >> type) {
if (type == "S") {
string name;
int studentNumber;
if (!(infile >> name >> studentNumber)) {
cerr << "读取学生信息失败。" << endl;
break;
}
Student student(name, studentNumber);
int subjectCount;
if (!(infile >> subjectCount)) {
cerr << "读取学生科目数量失败。" << endl;
break;
}
for (int i = 0; i < subjectCount; ++i) {
string subject;
float score;
if (!(infile >> subject >> score)) {
cerr << "读取学生科目成绩失败。" << endl;
break;
}
student.addScore(Score(subject, score));
}
students.push_back(student);
}
else if (type == "G") {
string name;
int studentNumber;
string projectName;
if (!(infile >> name >> studentNumber >> projectName)) {
cerr << "读取研究生信息失败。" << endl;
break;
}
GStudent gstudent(name, studentNumber, projectName);
int subjectCount;
if (!(infile >> subjectCount)) {
cerr << "读取研究生科目数量失败。" << endl;
break;
}
for (int i = 0; i < subjectCount; ++i) {
string subject;
float score;
if (!(infile >> subject >> score)) {
cerr << "读取研究生科目成绩失败。" << endl;
break;
}
gstudent.addScore(Score(subject, score));
}
gstudents.push_back(gstudent);
}
else {
cerr << "未知类型: " << type << endl;
break;
}
}
infile.close();
}
其次,对于保存数据文件,他的代码有些难以阅读,因为使用了一些奇怪的类,询问本人表示他也不记得之前是怎么思考和构思的了,在此基础上,我在保证总体框架不进行较大变动的同时,进行代码重构和改进,修正后的代码如下:
// 保存数据到文件
void Class::saveToFile(string filename) {
ofstream outfile(filename);
if (outfile.is_open()) {
for (const auto& student : students) {
outfile << "S " << student.name << " " << student.studentNumber << " " << student.scores.size();
for (const auto& score : student.scores) {
outfile << " " << score.subject << " " << score.score;
}
outfile << endl;
}
for (const auto& gstudent : gstudents) {
outfile << "G " << gstudent.name << " " << gstudent.studentNumber << " " << gstudent.projectName << " " << gstudent.scores.size();
for (const auto& score : gstudent.scores) {
outfile << " " << score.subject << " " << score.score;
}
outfile << endl;
}
outfile.close();
}
else {
cout << "打开文件错误。" << endl;
}
}
在代码中,我使用 ofstream 打开指定文件 filename,准备写入数据,如果文件打开失败,输出错误信息 "打开文件错误。"对于分别保存普通学生数据和研究生学生的数据,需要遍历 students和gstudents容器中的每个学生。将学生的类型标识 "S"、姓名 name、学号 studentNumber 和成绩数量 scores.size() 写入文件,遍历学生的成绩列表 scores,将每门课程的名称 subject 和分数 score 写入文件;将研究生的类型标识 "G"、姓名 name、学号 studentNumber、研究项目名称 projectName 和成绩数量 scores.size() 写入文件,遍历研究生的成绩列表 scores,将每门课程的名称 subject 和分数 score 写入文件,在最后使用 outfile.close() 关闭文件。
功能示例:
实验总结:
本次逆向开发的目标是对已有的学生成绩管理系统进行深入分析,理解其功能实现、数据结构及代码逻辑,并在此基础上进行优化、扩展或修复问题。通过逆向工程,我们能够更好地掌握系统的设计思路和技术细节,为后续的维护和升级提供支持。通过本次逆向开发,我们不仅修复了原有系统的问题,还为其未来的扩展和优化奠定了基础。逆向工程是一项复杂但极具价值的工作,它帮助我们更好地理解现有系统的设计思路,并为后续开发提供了宝贵经验。未来,我们将继续完善系统功能,提升其性能和用户体验,使其更好地服务于学生成绩管理的需求。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理