<span>课程管理系统设计(windows系统)</span>
1. 介绍
本项目使用 C++ 实现一个课程管理系统,这个项目会学习C++的很多特性,同时可以熟悉Linux下的C++语言编译方法及简单的Makefile编写(以后会完善)。
2. 项目需求
编写一个课程管理程序,程序具备下述基本功能:
- 程序运行后可以循环输入操作命令
- 操作命令输入0,打印出程序帮助信息,即每个操作命令的作用
- 操作命令输入1,打印出程序中存储的所有课程ID和课程名
- 输入2,打印出课程数量
- 输入3,打印出名字最长的课程信息,如果有多个相同长度的课程名请都打印出
- 输入4,删除最后一个课程,如果没有课程需要返回错误信息
- 输入5,退出程序
在上述基本功能之外的附加功能:
- 课程包含基础课,项目课,评估课不同的类型
- 课程ID唯一,需要自动生成
需求很简单,我们完全不需要复杂的面向对象也可以实现,但为了尽可能多的应用C++的知识点,会进行“过度设计”,目的是让大家尽可能多的了解C++的知识如何应用到实际的代码场景中。
一、针对这个项目,我们首先需要知道我们要做什么,要达到什么样的效果。根据项目要求,我们要做的就是输出一系列帮助信息,每一条帮助信息对应一个功能,对于每个功能,我们需要编写函数实现。项目要求:默认输出全部帮助信息,根据帮助信息中显示的命令行,输入相应的命令后,根据命令判断以及相应的函数处理,然后输出对应的操作。这个项目的重点是实现基础课的相关操作,为了能够体现项目的可扩展性和锻炼C++编程的技能,又定义了扩展接口,即项目课和评估课等不同的类型(即为基础课的派生类)。同时由于要求课程id是唯一的,需要将课程id定义为static变量,并在类的外部初始化。
二、根据项目功能要求,可以进行模块划分,分成两个模块:命令输入及处理和课程存储及管理。既而得出类的划分,分为课程类、课程管理类和命令管理类。课程对象存储每个课程的信息,课程管理对象对课程列表进行维护和管理,命令管理对象对命令进行管理。具体来说:
1、课程类:类成员需要包括课程Id和课程名称,类成员函数至少包括构造函数(参数为课程名称)、拷贝构造函数、返回和设置课程名称、获得课程id和打印课程信息函数(需要重载operator<<)等。
2、课程管理类:类成员至少包含课程列表,类成员函数至少包括构造函数(参数为课程对象vector容器)、获取课程数目、添加课程(参数为课程名称或课程对象)、删除课程(删除最新课程、指定id或指定名称)、打印课程列表、打印课程(最长名称、指定id或指定名称)等。
3、命令管理类:类成员至少需要命令列表和课程管理对象,类成员函数至少包括初始化函数(课程及命令信息初始化)、打印帮助信息和命令处理函数。 现在根据现有的类的划分,每个类都有.cpp和.h,然后在定义一个Cmd.h,其中定义所有支持的命令数字,还需要主函数main.cpp和Makefile文件。说到这里,这个项目的整体架构已经成形,接下来就是如何实现。 这里附上每个类.cpp的具体实现,一句一句敲上的,记录一下。
1 //------------------------------Course.cpp------------------------------------- 2 #include<iostream> 3 #include"Course.h" 4 using namespace std; 5 // Course 类成员函数 6 //初始化静态成员,默认第一个课程Id为1 7 int Course::currentId = 1; 8 //课程类构造函数 9 Course::Course() 10 { 11 //将currentId当前值赋值给id,再将currentId自增 12 id = currentId++; 13 //默认课程名称为空字符串 14 name = ""; 15 } 16 //课程类拷贝构造函数 17 Course::Course(const Course& course) 18 { 19 id = course.GetId(); 20 name = course.GetName(); 21 } 22 //打印课程信息 23 void Course::PrintInfo() const{ 24 cout<<"Course: "<< id <<" : "<< name << endl; 25 } 26 //友元函数:读取输入创建新的课程 27 void read(istream& is, Course& item){ 28 is >> item.name; 29 } 30 //友元函数:操作符<<重载函数,当cout<<输出课程信息时使用 31 ostream& operator<<(ostream& os, const Course& course) 32 { 33 os << "Course: "<< course.id <<" : " << course.name; 34 return os; 35 } 36 //ProjectCourse 类成员函数 37 //打印课程信息 38 void ProjectCourse::PrintInfo() const{ 39 cout<<"ProjectCourse: "<< id <<" : "<<name<<" : "<<tag<< endl; 40 } 41 //JudgeCourse 类成员函数 42 //打印课程信息 43 void JudgeCourse::PrintInfo() const{ 44 cout<<"JudgeCourse: "<< id <<" : "<<name<<" : "<<time<<endl; 45 }
1 //---------------------------------------CourseManager.cpp-------------------------- 2 #include<stdexcept> 3 #include<iostream> 4 #include "CourseManager.h" 5 6 using namespace std; 7 8 //构造函数,参数为课程vector 9 CourseManager::CourseManager(const vector<Course>& course){ 10 for(auto curs = course.begin();curs!=course.end();curs++) 11 courseList.push_back(*curs); 12 } 13 //添加课程函数(参数为课程名称) 14 void CourseManager::AddCourse(const string& courseName){ 15 Course course(courseName); 16 courseList.push_back(course); 17 } 18 //添加课程函数(参数为课程对象) 19 void CourseManager::AddCourse(const Course& course){ 20 courseList.push_back(course); 21 } 22 23 //删除最后一个课程 24 void CourseManager::RemoveLast(){ 25 try 26 { 27 //如果课程非空,则删除最后一门课程 28 if(!courseList.empty()){ 29 courseList.pop_back(); 30 cout<<"Deleted successfully!"<<endl; 31 } 32 //如果课程为空,则抛出异常被catch捕获 33 else{ 34 throw runtime_error("Deleted error, there is no course!"); 35 } 36 } 37 catch(runtime_error err){ 38 cout<<err.what()<<endl; 39 } 40 } 41 //删除课程:删除指定id的课程 42 void CourseManager::RemoveById(int id){ 43 int index = FindCourse(id); 44 if(index>0) 45 courseList.erase(courseList.begin()+index); 46 else 47 cout<<"NOT FOUND"<<endl; 48 } 49 //删除课程:删除指定名称的课程 50 void CourseManager::RemoveByName(const string& name){ 51 int index = FindCourse(name); 52 if(index>0) 53 courseList.erase(courseList.begin()+index); 54 else 55 cout<<"NOT FOUND"<<endl; 56 } 57 //打印课程列表 58 void CourseManager::PrintAllCourse(){ 59 cout<<"CourseList: "<<endl; 60 //遍历courseList,打印出所有course信息 61 for(auto curs = courseList.begin();curs!=courseList.end();curs++) 62 cout<<*curs<<endl; 63 } 64 //打印指定课程(指定id) 65 void CourseManager::PrintCourse(int id){ 66 int index = FindCourse(id); 67 if(index > 0) 68 cout<<courseList[index]<<endl; 69 else 70 cout<<"NOT FOUND"<<endl; 71 } 72 //打印指定课程(指定名称) 73 void CourseManager::PrintCourse(const string& name){ 74 int index = FindCourse(name); 75 if(index>0) 76 cout<<courseList[index]<<endl; 77 else 78 cout<<"NOT FOUND"<<endl; 79 } 80 //打印最长名称课程函数 81 void CourseManager::PrintLongName(){ 82 int maxLen = 0; 83 //遍历courseList,查找最长名称 84 for(auto curs = courseList.begin();curs!=courseList.end();curs++){ 85 int currentLen = curs->GetName().size(); 86 if(currentLen>maxLen) 87 maxLen = currentLen; 88 } 89 //遍历courseList,打印最长名称课程 90 for(auto curs = courseList.begin();curs!=courseList.end();curs++){ 91 if(curs->GetName().size()==maxLen) 92 cout<<*curs<<endl; 93 } 94 } 95 //根据id查找课程,返回课程在vector的索引 96 int CourseManager::FindCourse(int id){ 97 for(int i=0;i<courseList.size();i++){ 98 if(courseList[i].GetId()==id) 99 return i; 100 } 101 return -1; 102 } 103 //根据名称查找课程,返回课程在vector的索引 104 int CourseManager::FindCourse(const string& name){ 105 for(int i=0;i<courseList.size();i++){ 106 if(courseList[i].GetName()==name) 107 return i; 108 } 109 return -1; 110 }
1 //------------------------------------C***nager.cpp----------------------------- 2 #include<iostream> 3 #include "C***nager.h" 4 5 using namespace std; 6 7 //初始化函数 8 void C***nager::Init(){ 9 //初始化课程列表 10 manager.AddCourse("Linux"); 11 manager.AddCourse("NodeJS"); 12 manager.AddCourse("C++"); 13 manager.AddCourse("Python"); 14 manager.AddCourse("Spark"); 15 manager.AddCourse("Hadoop"); 16 manager.AddCourse("Ruby"); 17 manager.AddCourse("Java"); 18 //初始化命令列表 19 c***p.insert(pair<CourseCmd,string>(Cmd_PrintHelp,"Help Info")); 20 c***p.insert(pair<CourseCmd,string>(Cmd_PrintCourse,"Course List")); 21 c***p.insert(pair<CourseCmd,string>(Cmd_PrintCourseNum,"Course Number")); 22 c***p.insert(pair<CourseCmd,string>(Cmd_PrintLongName,"Longest Course Name")); 23 c***p.insert(pair<CourseCmd,string>(Cmd_RemoveLastCourse,"Remove Last Course")); 24 c***p.insert(pair<CourseCmd,string>(Cmd_exit,"Exit")); 25 } 26 //打印帮助信息 27 void C***nager::PrintAllHelp() const{ 28 cout<<"Cmd List: "<<endl; 29 for(auto iter = c***p.begin();iter!=c***p.end();iter++) 30 cout<<iter->first<<":"<<iter->second<<endl; 31 } 32 //根据命令查询帮助信息 33 void C***nager::PrintCmdHelp(const CourseCmd cmd) const{ 34 auto iter = c***p.find(cmd); 35 if(iter != c***p.end()) 36 cout<<iter->first<<":"<<iter->second<<endl; 37 else 38 cout<<"NOT FOUND"<<endl; 39 } 40 //处理命令操作,如果返回false则表示退出程序,其他情况返回true 41 bool C***nager::HandleCmd(const CourseCmd cmd){ 42 auto iter = c***p.find(cmd); 43 if(iter == c***p.end()){ 44 cout<<"NOT FOUND"<<endl; 45 return true; 46 } 47 switch(cmd){ 48 case Cmd_PrintHelp: PrintAllHelp();break; 49 case Cmd_PrintCourse: manager.PrintAllCourse();break; 50 case Cmd_PrintCourseNum: cout<<manager.GetCourseNum()<<endl;break; 51 case Cmd_PrintLongName: manager.PrintLongName();break; 52 case Cmd_RemoveLastCourse: manager.RemoveLast();break; 53 case Cmd_exit:return false; 54 default: return true; 55 } 56 return true; 57 }
1 //-----------------------------------------main.cpp--------------------------------- 2 #include "C***nager.h" 3 using namespace std; 4 5 int main() 6 { 7 //输入的命令 8 int cmd; 9 //创建命令管理对象 10 C***nager c***nager; 11 c***nager.Init(); 12 //打印帮助信息 13 c***nager.PrintAllHelp(); 14 cout<<"New Command: "; 15 //进入主循环并处理输入信息 16 while(cin>>cmd){ 17 if(cin.good()){ 18 bool exitCode = c***nager.HandleCmd((CourseCmd)cmd); 19 if(!exitCode) 20 return 0; 21 } 22 cout<<"---------------------------------"<<endl; 23 cout<<"New Command: "; 24 25 //清理输入流,避免刚才流中的字符影响后续输入 26 cin.clear(); 27 cin.ignore(); 28 } 29 return 0; 30 }
这里需要说明几点:
(1)在CourseManager类中需要存储课程列表,即包含一个Course容器,这里使用的vector容器,因为我们需要根据id和名称查找课程,并且需要删除最后的课程操作,顺序容器是最好的选择。
(2)在C***nager类中包含课程管理对象和命令行列表,在这里优先选择关联容器map,因为map中存储的对象既是key又是value,并且不允许有重复的key,map存储的对象是必须具有可排序性的,默认采用less排序行为。这里使用map既要存储命令值又要存储相应的提示信息。