C++大作业——学生选课系统优化版
- 前言
- 1.学生类和课程类的实现
- 2.输入输出流重载的实现
- 3.增删改查的实现
- 4.多级菜单的实现
- 5.选课和退选的实现
- 5.完整代码
前言
本文是对本人之前写过的一个学生选课系统的优化,整体上的逻辑是和我上一篇博客一样的(链接在此:https://blog.csdn.net/weixin_73870552/article/details/135298985?spm=1001.2014.3001.5501)。本文将具体介绍优化的部分,而不对基本的实现做过多阐述。
1.学生类和课程类的实现
课程类:
class Course
{// 输入输出流重载friend ostream& operator<<(ostream& out, const Course& c);friend fstream& operator<<(fstream& fout, const Course& c);// friend istream& operator>>(istream& in, Course& c); 这个重载不好实现,因为要查重friend fstream& operator>>(fstream& fin, Course& c);friend ostream& operator<<(ostream& out, const Student& s);public:// 构造函数初始化Course(): _id("None"), _name("None"), _type(0), _hour(0), _credir(0), _count(0){}// 相关接口const string c_id() { return _id; } // 编号的接口const string c_name() { return _name; } // 名字的接口int& c_count() { return _count; } // 选课人数的接口const bool c_type() { return _type; } // 类型接口// 文件操作void Write(vector<Course>& cour, int n) {} // 向文件写入数据int Read(vector<Course>& cour) {} // 从文件读取数据void Add(vector<Course>& cour) {} // 增void Delete(vector<Course>& cour) {} // 删void Modify(vector<Course>& cour) {} // 改void Lookup(vector<Course>& cour) {} // 查void Show(vector<Course>& cour) {} // 展示所有信息private:string _id; // 编号string _name; // 课程名bool _type; // 课程类型,1必修,0选修int _hour; // 学时int _credir; // 学分int _count; // 选课人数
};
1.这个课程类的成员变量中记录了课程的基本信息,其中编号和课程名,这种有字符串属性的数据,我使用了string
来存储。方便后续直接使用==
或!=
进行字符串的比较。
2.写了构造函数用来初始化数据。
3.将对课程的操作都写成成员函数,封装起来。后续的操作函数使用涉及到匿名对象,不了解的同学可以看下我之前的博客,这里就不再过多赘述了。
4.设计了一些接口,以便可以在类外访问私有成员变量。其中只有选课人数的接口是没有const
修饰的,因为要在类外对其进行修改。
5.声明了输入输出流的重载,用来简化代码。学生类的输出流重载也要在课程类中友元声明一下,因为后续要在这个重载函数中访问课程的一些私有变量,且在这个重载函数中接口会失效。
学生类:
class Student
{// 输入输出流重载friend ostream& operator<<(ostream& out, const Student& s);friend fstream& operator<<(fstream& fout, const Student& s);// friend istream& operator>>(istream& in, Student& s); 这个重载不好实现,因为要查重friend fstream& operator>>(fstream& fin, Student& s);
public:Student(): _id("None") // 学号, _class("None") // 班级, _name("None") // 姓名, _num_compulsory(0) , _num_elective(0){}void Write(vector<Student>& stu, int n) {}int Read(vector<Student>& stu) {}void Add(vector<Student>& stu) {}void Delete(vector<Student>& stu) {}void Modify(vector<Student>& stu) {}void Lookup(vector<Student>& stu) {}void Show(vector<Student>& stu) {}void Select(vector<Course>& cour, vector<Student>& stu) {} // 选课void Deselect(vector<Course>& cour, vector<Student>& stu) {} // 退选private:string _id; // 学号string _class; // 班级string _name; // 名字Course _cour_compulsory[SIZE_COMP]; // 必修课信息Course _cour_elective[SIZE_ELECT]; // 选修课信息int _num_compulsory; // 已选必修课数量int _num_elective; // 已选选修课数量
};
1.学生类的成员变量记录了学生的基本信息。其中,课程的信息我用两个数组存了起来,SIZE_COMP
和SIZE_ELECT
是两个宏,分别对应学生所能选择的必修课和选修课最大数量。
2.学生相关的操作函数作为成员函数,写在Studnet
类中,相比课程类多了选课和退选的模块。
3.写了构造函数来初始化数据。
4.声明了输入输出流重载,简化代码。
2.输入输出流重载的实现
课程类的输入输出重载:
ostream& operator<<(ostream& out, const Course& c)
{out << c._id << "\t" << c._name << "\t" << (c._type ? "必修" : "选修") << "\t"<< c._hour << "\t" << c._credir << "\t" << c._count;return out;
}fstream& operator<<(fstream& fout, const Course& c)
{fout << c._id << "\t" << c._name << "\t" << c._type << "\t"<< c._hour << "\t" << c._credir << "\t" << c._count;return fout;
}fstream& operator>>(fstream& fin, Course& c)
{fin >> c._id >> c._name >> c._type>> c._hour >> c._credir >> c._count;return fin;
}
要写一个io流的输出重载,还要写一个文件流的输入输出重载。由于课程类在输入信息时还需要判断课程的编号是否重复,输入流重载略显复杂,还不如直接输入,所以这里就不再实现了。
学生类的输入输出重载:
ostream& operator<<(ostream& out, const Student& s)
{out << s._id << "\t" << s._class << "\t" << s._name << "\t";for (int i = 0; i < SIZE_COMP; i++)out << s._cour_compulsory[i]._name << " ";out << "\t";for (int i = 0; i < SIZE_ELECT; i++)out << s._cour_elective[i]._name << " ";return out;
}fstream& operator<<(fstream& fout, const Student& s)
{fout << s._id << "\t" << s._class << "\t" << s._name << "\t";for (int i = 0; i < SIZE_COMP; i++)fout << s._cour_compulsory[i] << "\t";for (int i = 0; i < SIZE_ELECT; i++)fout << s._cour_elective[i] << "\t";fout << s._num_compulsory << "\t" << s._num_elective;return fout;
}fstream& operator>>(fstream& fin, Student& s)
{fin >> s._id >> s._class >> s._name;for (int i = 0; i < SIZE_COMP; i++)fin >> s._cour_compulsory[i];for (int i = 0; i < SIZE_ELECT; i++)fin >> s._cour_elective[i];fin >> s._num_compulsory >> s._num_elective;return fin;
}
学生类的io流输入和课程类同理,要判断学号是否重复,所以这里不再实现重载。应注意,学生类的io流输出重载中,课程信息只输出课程名;学生类的文件流输入输出重载中,复用了课程类的文件输入输出重载。
3.增删改查的实现
实现增删查改,需要借助两个全局的动态数组。vector<Course> cour
和vector<Student> stu
。
课程类:
class Course
{friend ostream& operator<<(ostream& out, const Course& c);friend fstream& operator<<(fstream& fout, const Course& c);// friend istream& operator>>(istream& in, Course& c); 这个重载不好实现,因为要查重friend fstream& operator>>(fstream& fin, Course& c);friend ostream& operator<<(ostream& out, const Student& s);public:Course(): _id("None"), _name("None"), _type(0), _hour(0), _credir(0), _count(0){}const string c_id() { return _id; } // 编号的接口const string c_name() { return _name; } // 名字的接口int& c_count() { return _count; } // 选课人数的接口const bool c_type() { return _type; } // 类型接口void Write(vector<Course>& cour, int n){fstream myfile;myfile.open("course.txt", ios::out | ios::binary);if (!myfile){cout << "course.txt can't open." << endl;abort();}myfile << n << endl << endl;for (int i = 0; i < n; i++)myfile << cour[i] << endl;myfile.close();}int Read(vector<Course>& cour){cour.clear();fstream myfile;myfile.open("course.txt", ios::in | ios::binary);if (!myfile){cout << "course.txt can't open." << endl;abort();}int n;myfile.seekg(0);myfile >> n;Course temp;for (int i = 0; i < n; i++){myfile >> temp;cour.push_back(temp);}myfile.close();return n;}void Add(vector<Course>& cour){system("cls");int n = Read(cour);char sign = '0';Course temp;cout << "============ 增加课程信息 ============" << endl;while (sign != 'n' && sign != 'N'){loop:cout << "ID: ";cin >> temp._id;// 查重int c = 0;while (c < n){c++;if (temp._id == cour[n - c]._id){cout << "编号重复,请重新输入。" << endl;goto loop;}}cout << "课程名: ";cin >> temp._name;cout << "课程类型(1必修,0选修): ";cin >> temp._type;cout << "学时: ";cin >> temp._hour;cout << "学分: ";cin >> temp._credir;n++;cour.push_back(temp);cout << "是否继续添加(y 或 n)";cin >> sign;}// 保存进文件Write(cour, n);}void Delete(vector<Course>& cour){system("cls");int n = Read(cour);int i = 0;string id;cout << "============ 删除课程 ============" << endl;cout << "请输入需要删除课程的编号: ";cin >> id;// 检查有没有这个课程while (i < n && id != cour[i]._id)i++;if (i == n){cout << "没找到该课程。" << endl;system("pause");return;}else{if (cour[i]._count){cout << "所选人数不为零,请联系所有学生退选后,再删除该课程。" << endl;system("pause");return;}}for (int j = i; j < n - 1; j++)cour[j] = cour[j + 1]; // 编译器自动生成的赋值运算符重载// 保存char sign;cout << "是否保存(y 或 n): ";cin >> sign;if (sign != 'n' && sign != 'N'){cour.erase(cour.end() - 1);Write(cour, n - 1);cout << "============ 删除成功 ============" << endl;system("pause");}}void Modify(vector<Course>& cour){system("cls");int n = Read(cour);string id;int i = 0;cout << "============ 修改课程信息 ============" << endl;cout << "请输入你想修改的课程id: ";cin >> id;// 看有没有这个课程cin >> id;while (i < n && id != cour[i]._id)i++;if (i == n){cout << "没有找到该课程。" << endl;system("pause");return;}else{if (cour[i]._count){cout << "所选人数不为零,请联系所有学生退选后,再修改课程信息。" << endl;system("pause");return;}}cout << "-------------------------------------------------" << endl;cout << "编号" << "\t" << "课程名" << "\t" << "类型" << "\t"<< "学时" << "\t" << "学分" << "\t" << "选课人数" << endl;cout << cour[i] << endl;cout << "-------------------------------------------------" << endl;cout << "请重新输入课程信息。" << endl;cout << "课程名: ";cin >> cour[i]._name;cout << "课程类型(1必修,0选修): ";cin >> cour[i]._type;cout << "学时: ";cin >> cour[i]._hour;cout << "学分: ";cin >> cour[i]._credir;// 保存char sign;cout << "是否保存修改(y 或 n): ";cin >> sign;if (sign != 'n' && sign != 'N'){Write(cour, n);cout << "============ 修改成功 ============" << endl;system("pause");}}void Lookup(vector<Course>& cour){system("cls");int n = Read(cour);int i = 0;string id;cout << "============ 查找课程 ============" << endl;cout << "请输入要查找课程的编号: ";cin >> id;// 检查是否存在while (i < n && id != cour[i]._id)i++;if (i == n){cout << "没有找到该课程。" << endl;}else{cout << "-------------------------------------------------" << endl;cout << "编号" << "\t" << "课程名" << "\t" << "类型" << "\t"<< "学时" << "\t" << "学分" << "\t" << "选课人数" << endl;cout << "-------------------------------------------------" << endl;cout << cour[i] << endl;}system("pause");}void Show(vector<Course>& cour){system("cls");int n = Read(cour);cout << "============ 展示所有课程信息 ============" << endl;cout << "-------------------------------------------------" << endl;cout << "编号" << "\t" << "课程名" << "\t" << "类型" << "\t"<< "学时" << "\t" << "学分" << "\t" << "选课人数" << endl;cout << "-------------------------------------------------" << endl;for (int i = 0; i < n; i++)cout << cour[i] << endl;system("pause");}private:string _id; // 编号string _name; // 课程名bool _type; // 课程类型,1必修,0选修int _hour; // 学时int _credir; // 学分int _count; // 选课人数
};
学生类:
class Student
{friend ostream& operator<<(ostream& out, const Student& s);friend fstream& operator<<(fstream& fout, const Student& s);// friend istream& operator>>(istream& in, Student& s); 这个重载不好实现,因为要查重friend fstream& operator>>(fstream& fin, Student& s);
public:Student(): _id("None") // 学号, _class("None") // 班级, _name("None") // 姓名, _num_compulsory(0) , _num_elective(0){}void Write(vector<Student>& stu, int n){fstream myfile;myfile.open("student.txt", ios::out | ios::binary);if (!myfile){cout << "student.txt can't open." << endl;abort();}myfile << n << endl << endl;for (int i = 0; i < n; i++)myfile << stu[i] << endl;myfile.close();}int Read(vector<Student>& stu){stu.clear();fstream myfile;myfile.open("student.txt", ios::in | ios::binary);if (!myfile){cout << "student.txt can't open." << endl;abort();}int n;myfile.seekg(0);myfile >> n;Student temp;for (int i = 0; i < n; i++){myfile >> temp;stu.push_back(temp);}myfile.close();return n;}void Add(vector<Student>& stu){system("cls");int n = Read(stu);char sign = '0';Student temp;cout << "============ 添加学生 ============" << endl;while (sign != 'n' && sign != 'N'){loop:cout << "学号: ";cin >> temp._id;// 检查重复int c = 0;while (c < n){c++;if (temp._id == stu[n - c]._id){cout << "学号重复,请重新输入。" << endl;goto loop;}}cout << "班级: ";cin >> temp._class;cout << "姓名: ";cin >> temp._name;n++;stu.push_back(temp);cout << "是否继续添加(y 或 n): ";cin >> sign;}Write(stu, n);}void Delete(vector<Student>& stu){system("cls");int n = Read(stu);int m = Course().Read(cour); // 课程数量int i = 0;string id;cout << "============ 删除学生 =============" << endl;cout << "请输入要删除学生的学号: ";cin >> id;while (i < n && id != stu[i]._id)i++;if (i == n){cout << "没有找到该学生。" << endl;system("pause");return;}// 找到该学生所选的全部课程,并让这些课程的选课人数-1if (stu[i]._num_compulsory){while (stu[i]._num_compulsory > 0){stu[i]._num_compulsory--;int c = 0;while (c < m){if (stu[i]._cour_compulsory[stu[i]._num_compulsory].c_id() == cour[c].c_id()){cour[c].c_count()--;break;}c++;}}}else if (stu[i]._num_elective){while (stu[i]._num_elective > 0){stu[i]._num_elective--;int c = 0;while (c < m){if (stu[i]._cour_elective[stu[i]._num_elective].c_id() == cour[c].c_id()){cour[c].c_count()--;break;}c++;}}}for (int j = i; j < n - 1; j++)stu[j] = stu[j + 1];// 保存char sign;cout << "请确定是否删除(y 或 n): ";cin >> sign;if (sign != 'n' && sign != 'N'){stu.erase(stu.end() - 1);Write(stu, n - 1);Course().Write(cour, m);cout << "============ 删除成功 ============" << endl;system("pause");}}void Modify(vector<Student>& stu){system("cls");int n = Read(stu);string id;int i = 0;cout << "============ 修改学生信息 ===========" << endl;cout << "请输入你想修改学生的学号: ";cin >> id;while (i < n && id != stu[i]._id)i++;if (i == n){cout << "找不到该学生。" << endl;system("pause");return;}cout << "-------------------------------------------------------" << endl;cout << "学号: " << stu[i]._id << endl;cout << "班级: " << stu[i]._class << endl;cout << "姓名: " << stu[i]._name << endl;cout << "-------------------------------------------------------" << endl;cout << "请重新输入学生信息。" << endl;cout << "班级: ";cin >> stu[i]._class;cout << "姓名: ";cin >> stu[i]._name;// 保存char sign;cout << "是否保存修改(y 或 n): ";cin >> sign;if (sign != 'n' && sign != 'N'){cout << "修改成功。" << endl;Write(stu, n);system("pause");}}void Lookup(vector<Student>& stu){system("cls");int n = Read(stu);Course().Read(cour);int i = 0;string id;cout << "============== 查找学生 ==============" << endl;cout << "请输入要查找学生的学号: ";cin >> id;while (i < n && id != stu[i]._id)i++;if (i == n){cout << "找不到该学生。" << endl;}else{cout << stu[i] << endl;}system("pause");}void Show(vector<Student>& stu){system("cls");int n = Read(stu);cout << "=============================== 展示所有学生信息 ==============================" << endl;cout << "学号" << "\t" << "班级" << "\t" << "姓名" << "\t" << "必修课" << "\t" << "选修课" << endl;cout << "-------------------------------------------------------------------------------" << endl;for (int i = 0; i < n; i++)cout << stu[i] << endl;system("pause");}private:string _id; // 学号string _class; // 班级string _name; // 名字Course _cour_compulsory[SIZE_COMP]; // 必修课信息Course _cour_elective[SIZE_ELECT]; // 选修课信息int _num_compulsory; // 已选必修课数量int _num_elective; // 已选选修课数量
};
可以发现,在查找和展示所有的函数中,我使用了输出流重载,让代码的可读性更高,也减少了代码量。
需要注意,和老版本不同,我没有再定义初始化文件的函数Input
。所以这需要我们提前创建好两个文本文件,并进行相应的数据录入,具体格式如下:
-
course.txt:
-
student.txt:
4.多级菜单的实现
int choose_user()
{
loop:system("cls");int c = 0;cout << "=================================================================" << endl;cout << "-------------- 欢迎来到学生选课系统 ---------------" << endl;cout << "请选择你的身份(1管理员 或 2学生)(输入0退出): ";cin >> c;string passwd;if (c == 1){cout << "请输入密码: ";cin >> passwd;if (passwd == string(PASSWORD)) // PASSWORD是一个宏,#define PASSWORD 密码{flag = 1;cout << "登入成功." << endl;system("pause");}else{cout << "密码错误." << endl;system("pause");goto loop;}}elseflag = 0;if (c < 0 || c > 2){cout << "请输入 0-2." << endl;system("pause");goto loop;}return c;
}void menu_course()
{int c = 0;while (1){system("cls");cout << "============ 课程菜单 ============" << endl;cout << " 1. 增加课程 " << endl;cout << " 2. 删除课程 " << endl;cout << " 3. 修改课程 " << endl;cout << " 4. 查找课程 " << endl;cout << " 5. 展示所有课程 " << endl;cout << " 0. 返回上级目录 " << endl;cout << "----------------------------------" << endl;cout << "请选择你的操作(0-6): ";cin >> c;switch (c){case 1:if (flag)Course().Add(cour);else{cout << "request denied." << endl;system("pause");}break;case 2:if (flag)Course().Delete(cour);else{cout << "request denied." << endl;system("pause");}break;case 3:if (flag)Course().Modify(cour);else{cout << "request denied." << endl;system("pause");}break;case 4:Course().Lookup(cour);break;case 5:Course().Show(cour);break;case 0:cout << "------------ 返回成功 ------------" << endl;system("pause");return;default:cout << "请输入 0-6." << endl;system("pause");break;}}
}void menu_student()
{int c = 0;while (1){system("cls");cout << "============ 学生菜单 ============" << endl;cout << " 1. 添加学生 " << endl;cout << " 2. 删除学生 " << endl;cout << " 3. 修改学生 " << endl;cout << " 4. 查找学生 " << endl;cout << " 5. 展示所有学生信息 " << endl;cout << " 6. 选课 " << endl;cout << " 7. 退选 " << endl;cout << " 0. 返回 " << endl;cout << "----------------------------------" << endl;cout << "请选择你的操作(0-7): ";cin >> c;switch (c){case 1:if (flag)Student().Add(stu);else{cout << "request denied." << endl;system("pause");}break;case 2:if (flag)Student().Delete(stu);else{cout << "request denied." << endl;system("pause");}break;case 3:if (flag)Student().Modify(stu);else{cout << "request denied." << endl;system("pause");}break;case 4:Student().Lookup(stu);break;case 5:Student().Show(stu);break;case 6:Student().Select(cour, stu);break;case 7:Student().Deselect(cour, stu);break;case 0:cout << "------------ 返回成功 ------------" << endl;system("pause");return;default:cout << "请输入 0-7." << endl;system("pause");break;}}
}int menu_main()
{int c = 0;while (1){system("cls");cout << "==========================================" << endl;cout << " 1. 课程菜单 " << endl;cout << " 2. 学生菜单 " << endl;cout << " 0. 回到上级目录 " << endl;cout << "请选择你的操作(0-2): ";cin >> c;if (c < 0 || c > 2){cout << "请输入 0-2." << endl;system("pause");}elsebreak;}return c;
}int main()
{cour.reserve(100);stu.reserve(100);while (1){loop:if (!choose_user()){cout << "=============== 谢谢使用!再见! ===============" << endl;return 0;}while (1){switch (menu_main()){case 1:menu_course();break;case 2:menu_student();break;case 0:cout << "=============== 返回成功 ===============" << endl;system("pause");goto loop;default:cout << "请输入 0-2." << endl;system("pause");break;}}}return 0;
}
这个菜单的实现基本和上一个版本相同,只不过密码处做了些许改动。定义了一个宏PASSWORD
,方便后续的维护。
5.选课和退选的实现
class Student
{
public:// ...// 选课void Select(vector<Course>& cour, vector<Student>& stu){system("cls");int n_cour = Course().Read(cour);int n_stu = Read(stu);int i = 0; // 学生int j = 0; // 课程string id_c; // 课程 string id_s; // 学生 cout << "============ 开始选课 ============" << endl;cout << "请输入学生学号: ";cin >> id_s;while (i < n_stu && stu[i]._id != id_s)i++;if (i == n_stu){cout << "找不到该学生。" << endl;system("pause");return;}cout << ("请输入想选择的课程编号: ");cin >> id_c;while (j < n_cour && cour[j].c_id() != id_c)j++;if (j == n_cour){cout << "找不到该课程" << endl;system("pause");return;}if (cour[j].c_count() >= 60){cout << "课程人数已达上限,选课失败。" << endl;system("pause");}else{if (cour[j].c_type())if (stu[i]._num_compulsory < 2){int c = 0;while (c < stu[i]._num_compulsory && cour[j].c_id() != stu[i]._cour_compulsory[c].c_id())c++;if (c < stu[i]._num_compulsory){cout << "请勿重复选择课程。" << endl;system("pause");return;}stu[i]._cour_compulsory[stu[i]._num_compulsory] = cour[j];stu[i]._num_compulsory++;cour[j].c_count()++;Course().Write(cour, n_cour);Write(stu, n_stu);cout << "选课成功。" << endl;system("pause");}else{cout << "必修课数量已达上限,选课失败。" << endl;system("pause");}else{if (stu[i]._num_elective < 1){int c = 0;while (c < stu[i]._num_elective && cour[j].c_id() != stu[i]._cour_elective[c].c_id())c++;if (c < stu[i]._num_elective){cout << "请勿重复选择课程。" << endl;system("pause");return;}stu[i]._cour_elective[stu[i]._num_elective] = cour[j];stu[i]._num_elective++;cour[j].c_count()++;Course().Write(cour, n_cour);Write(stu, n_stu);cout << "选课成功。" << endl;system("pause");}else{cout << "选修课数量已达上限,选课失败。" << endl;system("pause");}}}}// 退选void Deselect(vector<Course>& cour, vector<Student>& stu){system("cls");int n_cour = Course().Read(cour);int n_stu = Read(stu);int i = 0; // 学生int j = 0; // 课程string id_c; // 课程 string id_s; // 学生 cout << "============ 退选 ============" << endl;cout << "请输入学生学号: ";cin >> id_s;while (i < n_stu && stu[i]._id != id_s)i++;if (i == n_stu){cout << "找不到该学生。" << endl;system("pause");return;}cout << ("请输入想要退选的课程编号: ");cin >> id_c;while (j < n_cour && cour[j].c_id() != id_c)j++;if (j == n_cour){cout << "找不到该课程" << endl;system("pause");return;}if (stu[i]._num_compulsory){int c = stu[i]._num_compulsory - 1;while (c >= 0){if (stu[i]._cour_compulsory[c].c_id() == id_c){cour[j].c_count()--;stu[i]._num_compulsory--;stu[i]._cour_compulsory[c] = Course();// 重新排序for (; c < stu[i]._num_compulsory; c++)swap(stu[i]._cour_compulsory[c], stu[i]._cour_compulsory[c + 1]);Write(stu, n_stu);Course().Write(cour, n_cour);cout << "退选成功。" << endl;system("pause");return;}c--;}if (stu[i]._cour_elective){int c = stu[i]._num_elective - 1;while (c >= 0){if (stu[i]._cour_elective[c].c_id() == id_c){cour[j].c_count()--;stu[i]._num_elective--;stu[i]._cour_elective[c] = Course();for (; c < stu[i]._num_elective; c++)swap(stu[i]._cour_elective[c], stu[i]._cour_elective[c + 1]);Write(stu, n_stu);Course().Write(cour, n_cour);cout << "退选成功。" << endl;system("pause");return;}c--;}cout << "你并未选择该课程。" << endl;system("pause");return;}}if (stu[i]._cour_elective){int c = stu[i]._num_elective - 1;while (c >= 0){if (stu[i]._cour_elective[c].c_id() == id_c){cour[j].c_count()--;stu[i]._num_elective--;stu[i]._cour_elective[c] = Course();for (; c < stu[i]._num_elective; c++)swap(stu[i]._cour_elective[c], stu[i]._cour_elective[c + 1]);Write(stu, n_stu);Course().Write(cour, n_cour);cout << "退选成功。" << endl;system("pause");return;}c--;}cout << "你并未选择该课程。" << endl;system("pause");return;}}private:string _id; // 学号string _class; // 班级string _name; // 名字Course _cour_compulsory[SIZE_COMP]; // 必修课信息Course _cour_elective[SIZE_ELECT]; // 选修课信息int _num_compulsory; // 已选必修课数量int _num_elective; // 已选选修课数量
};
这一块的实现也和老版本没有多大区别,这里不再过多赘述,细节在我的上一篇博客中都有很详细的讲解。
5.完整代码
#define _CRT_SECURE_NO_WARNINGS#include<iostream>
#include<fstream>
#include<string>
#include<vector>using namespace std;#define SIZE_COMP 2 // 可以选择的必修课数量
#define SIZE_ELECT 1 // 可选择的选修课数量
#define PASSWORD "123456" // 管理员密码int flag = 0; // 确定身份状态的状态变量,0是学生,1是管理员,默认是0class Student; // 向前声明学生类class Course
{friend ostream& operator<<(ostream& out, const Course& c);friend fstream& operator<<(fstream& fout, const Course& c);// friend istream& operator>>(istream& in, Course& c); 这个重载不好实现,因为要查重friend fstream& operator>>(fstream& fin, Course& c);friend ostream& operator<<(ostream& out, const Student& s);public:Course(): _id("None"), _name("None"), _type(0), _hour(0), _credir(0), _count(0){}const string c_id() { return _id; } // 编号的接口const string c_name() { return _name; } // 名字的接口int& c_count() { return _count; } // 选课人数的接口const bool c_type() { return _type; } // 类型接口void Write(vector<Course>& cour, int n){fstream myfile;myfile.open("course.txt", ios::out | ios::binary);if (!myfile){cout << "course.txt can't open." << endl;abort();}myfile << n << endl << endl;for (int i = 0; i < n; i++)myfile << cour[i] << endl;myfile.close();}int Read(vector<Course>& cour){cour.clear();fstream myfile;myfile.open("course.txt", ios::in | ios::binary);if (!myfile){cout << "course.txt can't open." << endl;abort();}int n;myfile.seekg(0);myfile >> n;Course temp;for (int i = 0; i < n; i++){myfile >> temp;cour.push_back(temp);}myfile.close();return n;}void Add(vector<Course>& cour){system("cls");int n = Read(cour);char sign = '0';Course temp;cout << "============ 增加课程信息 ============" << endl;while (sign != 'n' && sign != 'N'){loop:cout << "ID: ";cin >> temp._id;// 查重int c = 0;while (c < n){c++;if (temp._id == cour[n - c]._id){cout << "编号重复,请重新输入。" << endl;goto loop;}}cout << "课程名: ";cin >> temp._name;cout << "课程类型(1必修,0选修): ";cin >> temp._type;cout << "学时: ";cin >> temp._hour;cout << "学分: ";cin >> temp._credir;n++;cour.push_back(temp);cout << "是否继续添加(y 或 n)";cin >> sign;}// 保存进文件Write(cour, n);}void Delete(vector<Course>& cour){system("cls");int n = Read(cour);int i = 0;string id;cout << "============ 删除课程 ============" << endl;cout << "请输入需要删除课程的编号: ";cin >> id;// 检查有没有这个课程while (i < n && id != cour[i]._id)i++;if (i == n){cout << "没找到该课程。" << endl;system("pause");return;}else{if (cour[i]._count){cout << "所选人数不为零,请联系所有学生退选后,再删除该课程。" << endl;system("pause");return;}}for (int j = i; j < n - 1; j++)cour[j] = cour[j + 1]; // 编译器自动生成的赋值运算符重载// 保存char sign;cout << "是否保存(y 或 n): ";cin >> sign;if (sign != 'n' && sign != 'N'){cour.erase(cour.end() - 1);Write(cour, n - 1);cout << "============ 删除成功 ============" << endl;system("pause");}}void Modify(vector<Course>& cour){system("cls");int n = Read(cour);string id;int i = 0;cout << "============ 修改课程信息 ============" << endl;cout << "请输入你想修改的课程id: ";cin >> id;// 看有没有这个课程cin >> id;while (i < n && id != cour[i]._id)i++;if (i == n){cout << "没有找到该课程。" << endl;system("pause");return;}else{if (cour[i]._count){cout << "所选人数不为零,请联系所有学生退选后,再修改课程信息。" << endl;system("pause");return;}}cout << "-------------------------------------------------" << endl;cout << "编号" << "\t" << "课程名" << "\t" << "类型" << "\t"<< "学时" << "\t" << "学分" << "\t" << "选课人数" << endl;cout << cour[i] << endl;cout << "-------------------------------------------------" << endl;cout << "请重新输入课程信息。" << endl;cout << "课程名: ";cin >> cour[i]._name;cout << "课程类型(1必修,0选修): ";cin >> cour[i]._type;cout << "学时: ";cin >> cour[i]._hour;cout << "学分: ";cin >> cour[i]._credir;// 保存char sign;cout << "是否保存修改(y 或 n): ";cin >> sign;if (sign != 'n' && sign != 'N'){Write(cour, n);cout << "============ 修改成功 ============" << endl;system("pause");}}void Lookup(vector<Course>& cour){system("cls");int n = Read(cour);int i = 0;string id;cout << "============ 查找课程 ============" << endl;cout << "请输入要查找课程的编号: ";cin >> id;// 检查是否存在while (i < n && id != cour[i]._id)i++;if (i == n){cout << "没有找到该课程。" << endl;}else{cout << "-------------------------------------------------" << endl;cout << "编号" << "\t" << "课程名" << "\t" << "类型" << "\t"<< "学时" << "\t" << "学分" << "\t" << "选课人数" << endl;cout << "-------------------------------------------------" << endl;cout << cour[i] << endl;}system("pause");}void Show(vector<Course>& cour){system("cls");int n = Read(cour);cout << "============ 展示所有课程信息 ============" << endl;cout << "-------------------------------------------------" << endl;cout << "编号" << "\t" << "课程名" << "\t" << "类型" << "\t"<< "学时" << "\t" << "学分" << "\t" << "选课人数" << endl;cout << "-------------------------------------------------" << endl;for (int i = 0; i < n; i++)cout << cour[i] << endl;system("pause");}private:string _id; // 编号string _name; // 课程名bool _type; // 课程类型,1必修,0选修int _hour; // 学时int _credir; // 学分int _count; // 选课人数
};vector<Course> cour;ostream& operator<<(ostream& out, const Course& c)
{out << c._id << "\t" << c._name << "\t" << (c._type ? "必修" : "选修") << "\t"<< c._hour << "\t" << c._credir << "\t" << c._count;return out;
}fstream& operator<<(fstream& fout, const Course& c)
{fout << c._id << "\t" << c._name << "\t" << c._type << "\t"<< c._hour << "\t" << c._credir << "\t" << c._count;return fout;
}fstream& operator>>(fstream& fin, Course& c)
{fin >> c._id >> c._name >> c._type>> c._hour >> c._credir >> c._count;return fin;
}class Student
{friend ostream& operator<<(ostream& out, const Student& s);friend fstream& operator<<(fstream& fout, const Student& s);// friend istream& operator>>(istream& in, Student& s); 这个重载不好实现,因为要查重friend fstream& operator>>(fstream& fin, Student& s);
public:Student(): _id("None") // 学号, _class("None") // 班级, _name("None") // 姓名, _num_compulsory(0) , _num_elective(0){}void Write(vector<Student>& stu, int n){fstream myfile;myfile.open("student.txt", ios::out | ios::binary);if (!myfile){cout << "student.txt can't open." << endl;abort();}myfile << n << endl << endl;for (int i = 0; i < n; i++)myfile << stu[i] << endl;myfile.close();}int Read(vector<Student>& stu){stu.clear();fstream myfile;myfile.open("student.txt", ios::in | ios::binary);if (!myfile){cout << "student.txt can't open." << endl;abort();}int n;myfile.seekg(0);myfile >> n;Student temp;for (int i = 0; i < n; i++){myfile >> temp;stu.push_back(temp);}myfile.close();return n;}void Add(vector<Student>& stu){system("cls");int n = Read(stu);char sign = '0';Student temp;cout << "============ 添加学生 ============" << endl;while (sign != 'n' && sign != 'N'){loop:cout << "学号: ";cin >> temp._id;// 检查重复int c = 0;while (c < n){c++;if (temp._id == stu[n - c]._id){cout << "学号重复,请重新输入。" << endl;goto loop;}}cout << "班级: ";cin >> temp._class;cout << "姓名: ";cin >> temp._name;n++;stu.push_back(temp);cout << "是否继续添加(y 或 n): ";cin >> sign;}Write(stu, n);}void Delete(vector<Student>& stu){system("cls");int n = Read(stu);int m = Course().Read(cour); // 课程数量int i = 0;string id;cout << "============ 删除学生 =============" << endl;cout << "请输入要删除学生的学号: ";cin >> id;while (i < n && id != stu[i]._id)i++;if (i == n){cout << "没有找到该学生。" << endl;system("pause");return;}// 找到该学生所选的全部课程,并让这些课程的选课人数-1if (stu[i]._num_compulsory){while (stu[i]._num_compulsory > 0){stu[i]._num_compulsory--;int c = 0;while (c < m){if (stu[i]._cour_compulsory[stu[i]._num_compulsory].c_id() == cour[c].c_id()){cour[c].c_count()--;break;}c++;}}}else if (stu[i]._num_elective){while (stu[i]._num_elective > 0){stu[i]._num_elective--;int c = 0;while (c < m){if (stu[i]._cour_elective[stu[i]._num_elective].c_id() == cour[c].c_id()){cour[c].c_count()--;break;}c++;}}}for (int j = i; j < n - 1; j++)stu[j] = stu[j + 1];// 保存char sign;cout << "请确定是否删除(y 或 n): ";cin >> sign;if (sign != 'n' && sign != 'N'){stu.erase(stu.end() - 1);Write(stu, n - 1);Course().Write(cour, m);cout << "============ 删除成功 ============" << endl;system("pause");}}void Modify(vector<Student>& stu){system("cls");int n = Read(stu);string id;int i = 0;cout << "============ 修改学生信息 ===========" << endl;cout << "请输入你想修改学生的学号: ";cin >> id;while (i < n && id != stu[i]._id)i++;if (i == n){cout << "找不到该学生。" << endl;system("pause");return;}cout << "-------------------------------------------------------" << endl;cout << "学号: " << stu[i]._id << endl;cout << "班级: " << stu[i]._class << endl;cout << "姓名: " << stu[i]._name << endl;cout << "-------------------------------------------------------" << endl;cout << "请重新输入学生信息。" << endl;cout << "班级: ";cin >> stu[i]._class;cout << "姓名: ";cin >> stu[i]._name;// 保存char sign;cout << "是否保存修改(y 或 n): ";cin >> sign;if (sign != 'n' && sign != 'N'){cout << "修改成功。" << endl;Write(stu, n);system("pause");}}void Lookup(vector<Student>& stu){system("cls");int n = Read(stu);Course().Read(cour);int i = 0;string id;cout << "============== 查找学生 ==============" << endl;cout << "请输入要查找学生的学号: ";cin >> id;while (i < n && id != stu[i]._id)i++;if (i == n){cout << "找不到该学生。" << endl;}else{cout << stu[i] << endl;}system("pause");}void Show(vector<Student>& stu){system("cls");int n = Read(stu);cout << "=============================== 展示所有学生信息 ==============================" << endl;cout << "学号" << "\t" << "班级" << "\t" << "姓名" << "\t" << "必修课" << "\t" << "选修课" << endl;cout << "-------------------------------------------------------------------------------" << endl;for (int i = 0; i < n; i++)cout << stu[i] << endl;system("pause");}void Select(vector<Course>& cour, vector<Student>& stu){system("cls");int n_cour = Course().Read(cour);int n_stu = Read(stu);int i = 0; // 学生int j = 0; // 课程string id_c; // 课程 string id_s; // 学生 cout << "============ 开始选课 ============" << endl;cout << "请输入学生学号: ";cin >> id_s;while (i < n_stu && stu[i]._id != id_s)i++;if (i == n_stu){cout << "找不到该学生。" << endl;system("pause");return;}cout << ("请输入想选择的课程编号: ");cin >> id_c;while (j < n_cour && cour[j].c_id() != id_c)j++;if (j == n_cour){cout << "找不到该课程" << endl;system("pause");return;}if (cour[j].c_count() >= 60){cout << "课程人数已达上限,选课失败。" << endl;system("pause");}else{if (cour[j].c_type())if (stu[i]._num_compulsory < 2){int c = 0;while (c < stu[i]._num_compulsory && cour[j].c_id() != stu[i]._cour_compulsory[c].c_id())c++;if (c < stu[i]._num_compulsory){cout << "请勿重复选择课程。" << endl;system("pause");return;}stu[i]._cour_compulsory[stu[i]._num_compulsory] = cour[j];stu[i]._num_compulsory++;cour[j].c_count()++;Course().Write(cour, n_cour);Write(stu, n_stu);cout << "选课成功。" << endl;system("pause");}else{cout << "必修课数量已达上限,选课失败。" << endl;system("pause");}else{if (stu[i]._num_elective < 1){int c = 0;while (c < stu[i]._num_elective && cour[j].c_id() != stu[i]._cour_elective[c].c_id())c++;if (c < stu[i]._num_elective){cout << "请勿重复选择课程。" << endl;system("pause");return;}stu[i]._cour_elective[stu[i]._num_elective] = cour[j];stu[i]._num_elective++;cour[j].c_count()++;Course().Write(cour, n_cour);Write(stu, n_stu);cout << "选课成功。" << endl;system("pause");}else{cout << "选修课数量已达上限,选课失败。" << endl;system("pause");}}}}void Deselect(vector<Course>& cour, vector<Student>& stu){system("cls");int n_cour = Course().Read(cour);int n_stu = Read(stu);int i = 0; // 学生int j = 0; // 课程string id_c; // 课程 string id_s; // 学生 cout << "============ 退选 ============" << endl;cout << "请输入学生学号: ";cin >> id_s;while (i < n_stu && stu[i]._id != id_s)i++;if (i == n_stu){cout << "找不到该学生。" << endl;system("pause");return;}cout << ("请输入想要退选的课程编号: ");cin >> id_c;while (j < n_cour && cour[j].c_id() != id_c)j++;if (j == n_cour){cout << "找不到该课程" << endl;system("pause");return;}if (stu[i]._num_compulsory){int c = stu[i]._num_compulsory - 1;while (c >= 0){if (stu[i]._cour_compulsory[c].c_id() == id_c){cour[j].c_count()--;stu[i]._num_compulsory--;stu[i]._cour_compulsory[c] = Course();// 重新排序for (; c < stu[i]._num_compulsory; c++)swap(stu[i]._cour_compulsory[c], stu[i]._cour_compulsory[c + 1]);Write(stu, n_stu);Course().Write(cour, n_cour);cout << "退选成功。" << endl;system("pause");return;}c--;}if (stu[i]._cour_elective){int c = stu[i]._num_elective - 1;while (c >= 0){if (stu[i]._cour_elective[c].c_id() == id_c){cour[j].c_count()--;stu[i]._num_elective--;stu[i]._cour_elective[c] = Course();for (; c < stu[i]._num_elective; c++)swap(stu[i]._cour_elective[c], stu[i]._cour_elective[c + 1]);Write(stu, n_stu);Course().Write(cour, n_cour);cout << "退选成功。" << endl;system("pause");return;}c--;}cout << "你并未选择该课程。" << endl;system("pause");return;}}if (stu[i]._cour_elective){int c = stu[i]._num_elective - 1;while (c >= 0){if (stu[i]._cour_elective[c].c_id() == id_c){cour[j].c_count()--;stu[i]._num_elective--;stu[i]._cour_elective[c] = Course();for (; c < stu[i]._num_elective; c++)swap(stu[i]._cour_elective[c], stu[i]._cour_elective[c + 1]);Write(stu, n_stu);Course().Write(cour, n_cour);cout << "退选成功。" << endl;system("pause");return;}c--;}cout << "你并未选择该课程。" << endl;system("pause");return;}}private:string _id; // 学号string _class; // 班级string _name; // 名字Course _cour_compulsory[SIZE_COMP]; // 必修课信息Course _cour_elective[SIZE_ELECT]; // 选修课信息int _num_compulsory; // 已选必修课数量int _num_elective; // 已选选修课数量
};vector<Student> stu;ostream& operator<<(ostream& out, const Student& s)
{out << s._id << "\t" << s._class << "\t" << s._name << "\t";for (int i = 0; i < SIZE_COMP; i++)out << s._cour_compulsory[i]._name << " ";out << "\t";for (int i = 0; i < SIZE_ELECT; i++)out << s._cour_elective[i]._name << " ";return out;
}fstream& operator<<(fstream& fout, const Student& s)
{fout << s._id << "\t" << s._class << "\t" << s._name << "\t";for (int i = 0; i < SIZE_COMP; i++)fout << s._cour_compulsory[i] << "\t";for (int i = 0; i < SIZE_ELECT; i++)fout << s._cour_elective[i] << "\t";fout << s._num_compulsory << "\t" << s._num_elective;return fout;
}fstream& operator>>(fstream& fin, Student& s)
{fin >> s._id >> s._class >> s._name;for (int i = 0; i < SIZE_COMP; i++)fin >> s._cour_compulsory[i];for (int i = 0; i < SIZE_ELECT; i++)fin >> s._cour_elective[i];fin >> s._num_compulsory >> s._num_elective;return fin;
}// --------------------------------------------------------------------------------------------------//int choose_user()
{
loop:system("cls");int c = 0;cout << "=================================================================" << endl;cout << "-------------- 欢迎来到学生选课系统 ---------------" << endl;cout << "请选择你的身份(1管理员 或 2学生)(输入0退出): ";cin >> c;string passwd;if (c == 1){cout << "请输入密码: ";cin >> passwd;if (passwd == string(PASSWORD)){flag = 1;cout << "登入成功." << endl;system("pause");}else{cout << "密码错误." << endl;system("pause");goto loop;}}elseflag = 0;if (c < 0 || c > 2){cout << "请输入 0-2." << endl;system("pause");goto loop;}return c;
}void menu_course()
{int c = 0;while (1){system("cls");cout << "============ 课程菜单 ============" << endl;cout << " 1. 增加课程 " << endl;cout << " 2. 删除课程 " << endl;cout << " 3. 修改课程 " << endl;cout << " 4. 查找课程 " << endl;cout << " 5. 展示所有课程 " << endl;cout << " 0. 返回上级目录 " << endl;cout << "----------------------------------" << endl;cout << "请选择你的操作(0-6): ";cin >> c;switch (c){case 1:if (flag)Course().Add(cour);else{cout << "request denied." << endl;system("pause");}break;case 2:if (flag)Course().Delete(cour);else{cout << "request denied." << endl;system("pause");}break;case 3:if (flag)Course().Modify(cour);else{cout << "request denied." << endl;system("pause");}break;case 4:Course().Lookup(cour);break;case 5:Course().Show(cour);break;case 0:cout << "------------ 返回成功 ------------" << endl;system("pause");return;default:cout << "请输入 0-6." << endl;system("pause");break;}}
}void menu_student()
{int c = 0;while (1){system("cls");cout << "============ 学生菜单 ============" << endl;cout << " 1. 添加学生 " << endl;cout << " 2. 删除学生 " << endl;cout << " 3. 修改学生 " << endl;cout << " 4. 查找学生 " << endl;cout << " 5. 展示所有学生信息 " << endl;cout << " 6. 选课 " << endl;cout << " 7. 退选 " << endl;cout << " 0. 返回 " << endl;cout << "----------------------------------" << endl;cout << "请选择你的操作(0-7): ";cin >> c;switch (c){case 1:if (flag)Student().Add(stu);else{cout << "request denied." << endl;system("pause");}break;case 2:if (flag)Student().Delete(stu);else{cout << "request denied." << endl;system("pause");}break;case 3:if (flag)Student().Modify(stu);else{cout << "request denied." << endl;system("pause");}break;case 4:Student().Lookup(stu);break;case 5:Student().Show(stu);break;case 6:Student().Select(cour, stu);break;case 7:Student().Deselect(cour, stu);break;case 0:cout << "------------ 返回成功 ------------" << endl;system("pause");return;default:cout << "请输入 0-7." << endl;system("pause");break;}}
}int menu_main()
{int c = 0;while (1){system("cls");cout << "==========================================" << endl;cout << " 1. 课程菜单 " << endl;cout << " 2. 学生菜单 " << endl;cout << " 0. 回到上级目录 " << endl;cout << "请选择你的操作(0-2): ";cin >> c;if (c < 0 || c > 2){cout << "请输入 0-2." << endl;system("pause");}elsebreak;}return c;
}int main()
{cour.reserve(100);stu.reserve(100);while (1){loop:if (!choose_user()){cout << "=============== 谢谢使用!再见! ===============" << endl;return 0;}while (1){switch (menu_main()){case 1:menu_course();break;case 2:menu_student();break;case 0:cout << "=============== 返回成功 ===============" << endl;system("pause");goto loop;default:cout << "请输入 0-2." << endl;system("pause");break;}}}return 0;
}