来源
为了学习代码的逆向开发,我向朋友要来了他C++的大作业代码,主要是开发一个学生成绩管理系统
源代码和运行截图
点击查看代码
#include <iostream>
#include <vector>
#include <fstream>
#include <string>
#include <algorithm>
#include <Windows.h>
using namespace std;
//光标
static void SetPos(int x, int y)
{
COORD pos = { x,y };
HANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(hout, pos);
}
//SetPos( , );
//字体颜色(11:青蓝色,12:红色,13:紫色,14:淡黄色,15:白色)
static int color(int c)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c);
return 0;
}
//color( , );
//分数类
class Score {
public:
string subject;//科目
double score;//成绩
Score() : subject(""), score(0.0) {}
Score(string subject, double score) : subject(subject), score(score) {}
friend istream& operator>>(istream& in, Score& s) {
in >> s.subject >> s.score;
return in;
}
friend ostream& operator<<(ostream& out, const Score& s) {
out << s.subject << " " << s.score;
return out;
}
};
//科目类
class Subject {
public:
string name;
Subject() : name("") {}
Subject(string name) : name(name) {}
friend istream& operator>>(istream& in, Subject& s) {
in >> s.name;
return in;
}
friend ostream& operator<<(ostream& out, const Subject& s) {
out << s.name;
return out;
}
};
//项目类
class Project {
public:
string title;
Project() : title("") {}
Project(string title) : title(title) {}
friend istream& operator>>(istream& in, Project& p) {
in >> p.title;
return in;
}
friend ostream& operator<<(ostream& out, const Project& p) {
out << p.title;
return out;
}
};
//学生类
class Student {
public:
int id;
string name;
vector<Score> scores;
Student() : id(0), name("") {}
Student(int id, string name, vector<Score> scores) : id(id), name(name), scores(scores) {}
virtual ~Student() {}
virtual void display(int &i) const {
SetPos(45, i);
cout << "学号: " << id << " 姓名: " << name << endl;
for (const auto& score : scores) {
i += 1;
SetPos(45, i);
cout << "科目: " << score.subject << " 成绩: " << score.score << endl;;
}
}
friend istream& operator>>(istream& in, Student& s) {
in >> s.id >> s.name;
int num_scores;
in >> num_scores;
s.scores.resize(num_scores);
for (int i = 0; i < num_scores; ++i) {
in >> s.scores[i];
}
return in;
}
friend ostream& operator<<(ostream& out, const Student& s) {
out << s.id << " " << s.name << " " << s.scores.size() << endl;
for (const auto& score : s.scores) {
out << score << endl;
}
return out;
}
};
//研究生类
class GStudent : public Student {
public:
Project project;
GStudent() : Student(), project("") {}
GStudent(int id, string name, vector<Score> scores, Project project)
: Student(id, name, scores), project(project) {}
void display(int &i) const override {
Student::display(i);
i += 1;
SetPos(45, i);
cout << "课题: " << project.title;
}
friend istream& operator>>(istream& in, GStudent& gs) {
in >> static_cast<Student&>(gs);
in >> gs.project;
return in;
}
friend ostream& operator<<(ostream& out, const GStudent& gs) {
out << static_cast<const Student&>(gs);
out << gs.project << endl;
return out;
}
};
//班级类
class Class {
public:
string name;
vector<Student*> students;
Class() : name("") {}
Class(string name, vector<Student*> students) : name(name), students(students) {}
~Class() {
for (auto student : students) {
delete student;
}
}
//学生数据添加
void addStudent(Student* student) {
students.push_back(student);
}
//学生数据删除
void deleteStudent(int id) {
auto it = remove_if(students.begin(), students.end(), [id](Student* s) { return s->id == id; });
if (it != students.end()) {
delete* it;
students.erase(it);
}
}
//学生数据查询
void modifyStudent(int id, Student* newStudent) {
for (auto& student : students) {
if (student->id == id) {
delete student;
student = newStudent;
break;
}
}
}
//班级数据显示
void display(int i) const {
SetPos(45, i);
color(11);
cout << "班级: " << name << endl;
color(14);
for (const auto& student : students) {
i += 2;
student->display(i);
}
}
//文件保存
void saveToFile(const string& filename) const {
ofstream file(filename.c_str());
if (file.is_open()) {
file << name << endl;
file << students.size() << endl;
for (const auto& student : students) {
if (dynamic_cast<GStudent*>(student)) {
file << "G" << endl;
file << *dynamic_cast<GStudent*>(student);
}
else {
file << "S" << endl;
file << *student;
}
}
file.close();
}
}
//文件下载
void loadFromFile(const string& filename) {
ifstream file(filename.c_str());
if (file.is_open()) {
file >> name;
int num_students;
file >> num_students;
students.resize(num_students);
for (int i = 0; i < num_students; ++i) {
char type;
file >> type;
if (type == 'G') {
students[i] = new GStudent();
}
else {
students[i] = new Student();
}
file >> *students[i];
}
file.close();
}
}
//根据学号排列学生
void sortStudentsByID() {
sort(students.begin(), students.end(), [](const Student* s1, const Student* s2) {
return s1->id < s2->id;
});
}
};
//目录
void displayMenu() {
system("cls");
SetPos(45,7);
color(11);
cout << "学生成绩管理系统" << endl;
SetPos(45,9);
cout << "1. 显示所有学生" << endl;
SetPos(45,11);
cout << "2. 添加学生" << endl;
SetPos(45,13);
cout << "3. 删除学生" << endl;
SetPos(45,15);
cout << "4. 修改学生" << endl;
SetPos(45,17);
cout << "5. 查询学生" << endl;
SetPos(45,19);
cout << "6. 按学号排序" << endl;
SetPos(45,21);
cout << "7. 保存数据到文件" << endl;
SetPos(45,23);
cout << "8. 退出" << endl;
SetPos(45,25);
cout << "请输入您的选择: ";
}
int main() {
Class myClass;
myClass.loadFromFile("class_data.txt");
int choice;
do {
displayMenu();
cin >> choice;
switch (choice) {
case 1:
system("cls");
myClass.display(7);
system("pause");
break;
case 2: {
char type;
system("cls");
SetPos(30, 7);
cout << "请输入学生类型(S: 普通学生, G: 研究生): ";
cin >> type;
if (type == 'G') {
GStudent* newGStudent = new GStudent();
SetPos(30, 9);
cout << "请输入研究生信息(学号 姓名 科目数 科目 成绩 ... 课题): ";
SetPos(30, 11);
cin >> *newGStudent;
myClass.addStudent(newGStudent);
SetPos(30, 13);
cout << "已输入学生信息,请在主界面保存数据";
SetPos(30, 15);
system("pause");
}
else if(type=='S') {
Student* newStudent = new Student();
SetPos(30, 9);
cout << "请输入学生信息(学号 姓名 科目数 科目 成绩 ...): ";
SetPos(30, 11);
cin >> *newStudent;
myClass.addStudent(newStudent);
SetPos(30, 13);
cout << "已输入学生信息,请在主界面保存数据";
SetPos(30, 15);
system("pause");
}
else {
SetPos(30, 9);
cout << "输入有误";
SetPos(30, 11);
system("pause");
}
break;
}
case 3: {
system("cls");
int id;
SetPos(30, 9);
cout << "请输入要删除的学生学号: ";
cin >> id;
myClass.deleteStudent(id);
SetPos(30, 10);
cout << "已删除";
SetPos(30, 12);
system("pause");
break;
}
case 4: {
int id;//目标学号
system("cls");
SetPos(30, 7);
cout << "请输入要修改的学生学号: ";
cin >> id;
char type;
SetPos(30, 8);
cout << "请输入学生类型(S: 普通学生, G: 研究生): ";
cin >> type;
if (type == 'G') {
GStudent* modifiedGStudent = new GStudent();
SetPos(30, 10);
cout << "请输入新的研究生信息(学号 姓名 科目数 科目 成绩 ... 课题): ";
SetPos(30, 11);
cin >> *modifiedGStudent;
myClass.modifyStudent(id, modifiedGStudent);
SetPos(30, 13);
cout << "已修改学生信息,请在主界面保存数据";
SetPos(30, 15);
system("pause");
}
else if (type == 'S') {
Student* modifiedStudent = new Student();
SetPos(30, 10);
cout << "请输入新的学生信息(学号 姓名 科目数 科目 成绩 ...): ";
SetPos(30, 11);
cin >> *modifiedStudent;
myClass.modifyStudent(id, modifiedStudent);
SetPos(30, 13);
cout << "已修改学生信息,请在主界面保存数据";
SetPos(30, 15);
system("pause");
}
else {
SetPos(30, 9);
cout << "输入有误";
SetPos(30, 11);
system("pause");
}
break;
}
case 5: {
system("cls");
int i = 9;
int id;
SetPos(45, i);
cout << "请输入要查询的学生学号: ";
cin >> id;
i += 2;
auto it = find_if(myClass.students.begin(), myClass.students.end(), [id](const Student* s) { return s->id == id; });
if (it != myClass.students.end()) {
(*it)->display(i);
}
else {
SetPos(45, i);
cout << "未找到该学生。" << endl;
}
i += 1;
SetPos(45, i);
system("pause");
break;
}
case 6:
myClass.sortStudentsByID();
SetPos(45, 27);
cout << "学生已按学号排序。" << endl;
SetPos(45, 28);
system("pause");
break;
case 7:
myClass.saveToFile("class_data.txt");
SetPos(45, 27);
cout << "数据已保存到文件。" << endl;
SetPos(45, 28);
system("pause");
break;
case 8:
SetPos(45, 27);
cout << "退出系统。" << endl;
break;
default:
SetPos(45, 27);
color(12);
cout << "无效选择,请重新输入。" << endl;
}
} while (choice != 8);
return 0;
}

主要问题
对于用户输入的数据没有做限制,没有对数据的格式和范围做筛查,也没有对可能的错误数据做避免
修改代码
点击查看代码
// 在文件开头的 include 部分添加
#include <stdexcept>
// 修改 Score 类
class Score {
public:
//源代码
bool isValid() const {
return score >= 0.0 && score <= 100.0;
}
friend istream& operator>>(istream& in, Score& s) {
in >> s.subject >> s.score;
if (!s.isValid()) {
throw invalid_argument("成绩必须在0-100之间");
}
return in;
}
};
// 修改 Student 类
class Student {
public:
// 源代码
bool isValidId(int id) const {
return id > 0;
}
bool isValidName(const string& name) const {
return !name.empty() && name.length() <= 20;
}
friend istream& operator>>(istream& in, Student& s) {
int tempId;
string tempName;
in >> tempId >> tempName;
if (!s.isValidId(tempId)) {
throw invalid_argument("学号必须为正数");
}
if (!s.isValidName(tempName)) {
throw invalid_argument("姓名不能为空且长度不能超过20个字符");
}
s.id = tempId;
s.name = tempName;
int num_scores;
in >> num_scores;
if (num_scores <= 0) {
throw invalid_argument("科目数量必须为正数");
}
s.scores.clear();
s.scores.reserve(num_scores);
for (int i = 0; i < num_scores; ++i) {
Score score;
in >> score;
s.scores.push_back(score);
}
return in;
}
};
// 源代码
// 在 main 函数中修改 case 2
case 2: {
char type;
system("cls");
SetPos(30, 7);
cout << "请输入学生类型(S: 普通学生, G: 研究生): ";
cin >> type;
try {
if (type == 'G') {
GStudent* newGStudent = new GStudent();
SetPos(30, 9);
cout << "请输入研究生信息(学号 姓名 科目数 科目 成绩 ... 课题): ";
SetPos(30, 11);
cin >> *newGStudent;
myClass.addStudent(newGStudent);
SetPos(30, 13);
cout << "已输入学生信息,请在主界面保存数据";
}
else if (type == 'S') {
Student* newStudent = new Student();
SetPos(30, 9);
cout << "请输入学生信息(学号 姓名 科目数 科目 成绩 ...): ";
SetPos(30, 11);
cin >> *newStudent;
myClass.addStudent(newStudent);
SetPos(30, 13);
cout << "已输入学生信息,请在主界面保存数据";
}
else {
throw invalid_argument("无效的学生类型");
}
}
catch (const invalid_argument& e) {
SetPos(30, 13);
color(12);
cout << "输入错误: " << e.what();
color(15);
}
catch (...) {
SetPos(30, 13);
color(12);
cout << "发生未知错误,请重试";
color(15);
}
SetPos(30, 15);
system("pause");
break;
}
// 修改 case 4 (修改学生信息部分)
case 4: {
int id;
system("cls");
SetPos(30, 7);
cout << "请输入要修改的学生学号: ";
cin >> id;
char type;
SetPos(30, 8);
cout << "请输入学生类型(S: 普通学生, G: 研究生): ";
cin >> type;
try {
if (type == 'G') {
GStudent* modifiedGStudent = new GStudent();
SetPos(30, 10);
cout << "请输入新的研究生信息(学号 姓名 科目数 科目 成绩 ... 课题): ";
SetPos(30, 11);
cin >> *modifiedGStudent;
myClass.modifyStudent(id, modifiedGStudent);
SetPos(30, 13);
cout << "已修改学生信息,请在主界面保存数据";
}
else if (type == 'S') {
Student* modifiedStudent = new Student();
SetPos(30, 10);
cout << "请输入新的学生信息(学号 姓名 科目数 科目 成绩 ...): ";
SetPos(30, 11);
cin >> *modifiedStudent;
myClass.modifyStudent(id, modifiedStudent);
SetPos(30, 13);
cout << "已修改学生信息,请在主界面保存数据";
}
else {
throw invalid_argument("无效的学生类型");
}
}
catch (const invalid_argument& e) {
SetPos(30, 13);
color(12);
cout << "输入错误: " << e.what();
color(15);
}
catch (...) {
SetPos(30, 13);
color(12);
cout << "发生未知错误,请重试";
color(15);
}
SetPos(30, 15);
system("pause");
break;
}


总结
我追加了输入验证系统,改进了文件存储的格式,使程序可以识别用户的输入错误并且报错;
我认为逆向软件开发最重要的就是理解,必须先读懂别人的源代码才有可能进行进一步开发,这也体现了注释的必要性,可以加强程序的可读性;
本次开发中最困难的就是如何在不破坏原有输入方法的同时加入验证功能,我选用了try-catch的问题读取方法,这样相比一味的用if读取更加节省程序的步骤;