C++实现的数据结构大作业之家谱管理系统欢迎界面:主菜单:其他一些运行结果不在一一列举,一面篇幅过长。
这个家谱管理系统,支持键盘操作,所有的操作都可以用键盘完成,如选择功能、退出等。
代码的实现使用的是c++11标准,与旧标准或有不同。
类的头文件:#include<iostream>#include<string>using std::string;using std::ostream;using std::istream;struct BirthDay{//生日结构变量int year=0;int month=0;int day=0;};class Member{friend class FamilySystem;friend ostream&operator<<(ostream&out,const Member&m);//输出成员信息到控制台friend istream&operator>>(istream&, Member&m);//从文件中读入成员信息public:Member()=default;//使用默认合成构造函数void setE();//通过控制台输入给成员赋值~Member()=default;//使用默认合成析构函数Member*pson=nullptr;//指向儿子的指针Member*pbro=nullptr;//指向兄弟的指针private://成员的各种数据string name="未知";string birthPlace="未知";BirthDay birthDay;string sex="未知";float height=0;int age=0;string education="未知";string job="未知";string father="未知";};class FamilySystem{public:FamilySystem()=default;//使用默认合成构造函数~FamilySystem(){delete root;}bool fileInit();//从文件中读取数据int menu();//显示菜单(功能)bool addMem();//增加成员bool deleteMem();//删除成员bool demandMem();//查询bool demandAve();//查询家庭整体情况bool modifyMem();//修改成员bool showFamily();//展示整个家谱bool saveFile();//保存信息到文件中,将使用两个文件,一个保存成员信息,一个保存家谱整体信息Member*root=nullptr;//家谱数结构的根节点指针?private:int total=0;//总人数float aveAge=0;//平均年龄float aveHeight=0;//平均身高float aveMember=0;//家庭平均人数float ratio=0;//男女比例void calculate();//计算平均值等Member*seek(string);//查找是否存在成员void display(Member*,int);//先序遍历树,用于显示所有成员};extern string gets();//用于接收键盘的输入类的函数及相关操作函数的实现#include<iomanip>#include<vector>#include<fstream>#include<sstream>#include<conio.h>#include"SystemClass.h"using std::cin;using std::cout;using std::endl;using std::setw;using std::vector;using std::ios;using std::ifstream;using std::ofstream;using std::istringstream;string gets(){//从控制台获取字符等信息string s;while(int n=_getch()){if(n==27){throw27;break;}else if(n==13){cout<<endl;break;}else if(n==8){s.pop_back();cout<<"\b \b";}else{cout<<char(n);s.push_back(char(n));}}return s;}//与BirthDay相关的函数istream&operator>>(istream&in,BirthDay&b){//从文件中读取生日的信息in>>b.year>>b.month>>b.day;return in;}ostream&operator<<(ostream&out,BirthDay&b){ out<<b.year<<" "<<b.month<<" "<<b.day;return out;}//与Member相关的函数void Member::setE(){try{istringstream sin;cout<<setw(20)<<"请输入成员信息:\n"<<endl<<setw(20)<<"姓名:";sin.str(gets());sin>>name;cout<<endl<<setw(20)<<"出生地:"; sin.str(gets());sin.clear();sin>> birthPlace;cout<<endl<<setw(20)<<"生日:"; sin.str(gets());while(true){sin.clear();sin>>birthDay.year>>birthDay.month>>birthDay.day;if(!sin||birthDay.year>2014|| birthDay.month>12||birthDay.day>31){cout<<"输入错误,请重新输入:";sin.str(gets());}else break;}cout<<endl<<setw(20)<<"性别:";sin.str(gets());sin.clear();while(sin>>sex){if(sex=="male"||sex=="female"|| sex=="男"||sex=="女")break;else{cout<<"性别必须是male、female、男或者女,请重新输入:";sin.str(gets());sin.clear();}}cout<<endl<<setw(20)<<"身高(厘米):"; sin.str(gets());sin.clear();while(!(sin>>height)){cout<<"输入错误,请重新输入:";sin.str(gets());sin.clear();}cout<<endl<<setw(20)<<"年龄:";sin.str(gets());sin.clear();while(!(sin>>age)||age>300){cout<<"输入错误,请重新输入:"<<endl;sin.str(gets());sin.clear();}cout<<endl<<setw(20)<<"学历:";sin.str(gets());sin.clear();sin>>education;cout<<endl<<setw(20)<<"工作:";sin.str(gets());sin.clear();sin>>job;cout<<endl<<setw(20)<<"父亲:";sin.str(gets());sin.clear();sin>>father;}catch(int ex){throw ex;}return;}ostream&operator<<(ostream&out,const Member &m){out<<endl<<setw(25)<<"该成员信息如下:" <<endl<<endl<<setw(25)<<"姓名: "<<<<endl<< endl<<setw(25)<<"出生地: "<<m.birthPlace <<endl<<endl<<setw(25)<<"生日: "<<m.birthDay.year <<" "<<m.birthDay.month<<" "<<m.birthDay.day<<endl<<endl<<setw(25)<<"性别: "<<m.sex<<endl<< endl<<setw(25)<<"身高(厘米): "<<m.height <<endl<<endl<<setw(25)<<"年龄: "<<m.age<<endl<< endl<<setw(25)<<"学历: "<<cation<< endl<<endl<<setw(25)<<"工作: "<<m.job<<endl<< endl<<setw(25)<<"父亲: "<<m.father<< endl;return out;}istream&operator>>(istream&in,Member&m){in>>>>m.birthPlace>>m.birthDay>>m.sex>>m.height>>m.age>>cation>>m.job>>m.father;return in;}//FamilySystem的成员函数int FamilySystem::menu(){cout<<"\n\n";cout<<" ---------------------------\n";cout<<" | |\n";cout<<" | 系统菜单 |\n";cout<<" | |\n";cout<<" | 1.查询成员 |\n";cout<<" | |\n";cout<<" | 2.查询家庭整体情况 |\n";cout<<" | |\n";cout<<" | 3.添加成员 |\n";cout<<" | |\n";cout<<" | 4.删除成员 |\n";cout<<" | |\n";cout<<" | 5.修改成员信息 |\n";cout<<" | |\n";cout<<" | 6.显示整个家谱 |\n";cout<<" | |\n";cout<<" | 按ESC退出系统 |\n";cout<<" | |\n";cout<<" ---------------------------\n";cout<<" ";int n=0;while(1){n=_getch();if(n==27)break;if(n>=49&&n<=54)break;cout<<"\n请按下1、2、3、4、5、6选择或按ESC键退出!"<<endl;}return n;}bool FamilySystem::fileInit(){ifstream infile;//打开存储成员信息的文件,读入成员信息infile.open("Members.txt",ios::in|ios::_Nocreate);if(!infile){return false;}vector<Member*>genely;//保存这一代成员的指针vector<Member*>next;//保存下一代成员的指针Member*temp=new Member;//临时保存输入的成员信息root=temp;//第一个赋给rootgenely.push_back(root);next.push_back(root);while(infile>>*temp){//将成员放入家族树中,成员的父亲必须是这一代的成员for(auto i=genely.begin();i!= genely.end();++i){if((*i)->name==temp->father){if((*i)->pson==nullptr)(*i)->pson=temp;else{Member*p=(*i)->pson;while(p->pbro!=nullptr)p=p->pbro;p->pbro=temp;}next.push_back(temp);temp=nullptr;break;}}//若成员的父亲不是这一代的成员,则将next赋予genely,迭进下一代if(temp!=nullptr){genely=next;//迭进下一代next.clear();//清空nextfor(auto i:genely){//将成员放入家族树中if(i->name==temp->father){i->pson=temp;next.push_back(temp);temp=nullptr;break;}}}//动态创建临时成员变量,用于存储输入的下一个成员的信息temp=new Member;}infile.close();//关闭存储成员信息的文件//若文件中没有成员信息,将root置为空if(root->name=="未知")root=nullptr;//打开存储家庭总体信息的文件,读入信息infile.open("Wholes.txt",ios::in|ios::_Nocreate);if(!infile){return false;}infile>>total>>aveAge>>aveHeight>> aveMember>>ratio;infile.close();//关闭文件return true;}Member*FamilySystem::seek(string nam){ //若家谱树为空,返回空指针if(root==nullptr)return nullptr;//若root不为空,继续查找Member*store=nullptr;//存储返回的指针vector<Member*>genely;//存储这一代的指针genely.push_back(root);vector<Member*>next;//存储下一代的指针for(;;){//循环查找//在这一代中查找,若找到,跳到最后for(auto p:genely){if(p->name==nam){store=p;goto End;}}//判断下一代是否为空,若为空,跳到最后int jubge=0;for(auto p:genely){if(p->pson!=nullptr)++jubge;}if(jubge==0)goto End;//找到下一代for(auto p:genely){Member*temp=p->pson;if(temp==nullptr)continue;else{next.push_back(temp);while(temp->pbro!=nullptr){next.push_back(temp->pbro);temp=temp->pbro;}}}genely=next;next.clear();}End:return store;}bool FamilySystem::addMem(){cout<<"3.添加成员.\n";Member*temp=new Member;try{temp->setE();//输入成员信息}catch(int){return false;}//检查其父亲是否存在Member*jubge=seek(temp->father);if(root==nullptr)root=temp;else if(jubge==nullptr){cout<<"\n 家谱中没有此人父亲的信息!无法插入!";cout<<"\n 请确认此人是否此家族中的人!";}else{if(seek(temp->name)!=nullptr){cout<<"\n\n 此人已存在于家谱中!" <<endl;}else{cout<<" 按ENTER确定添加并返回菜单\n 按ESC取消添加并返回菜单";for(;;){int j=_getch();if(j==27){delete temp;break;}if(j==13){if(jubge->pson==nullptr)jubge->pson=temp;else{Member*p=jubge->pson;while(p->pbro!=nullptr)p=p->pbro;p->pbro=temp;}cout<<"\n 保存成功!\n";break;}}}}cout<<"\n按ESC键返回菜单";for(;;){if(_getch()==27)break;}return true;}bool FamilySystem::deleteMem(){cout<<"4.删除成员.\n\n";cout<<" 请输入姓名:";string na;try{istringstream sin(gets());sin>>na;}catch(int){goto end;}Member*temp=seek(na);if(temp==nullptr)cout<<"\n\n 家谱中没有此人!\n\n";else{if(temp->pson!=nullptr){cout<<"\n\n 此人有后代!若删除此人其后代将同时被删!";cout<<"\n\n 按ENTER继续\n 按ESC退出";for(;;){int j=_getch();if(j==27)return false;if(j==13)break;}}Member*p=seek(temp->father);if(p==nullptr)root=nullptr;else if(temp==p->pson)p->pson=nullptr;else{while(p->pbro!=temp){p=p->pbro;}p->pbro=nullptr;}cout<<"\n\n 删除成功!\n\n";}cout<<"\n按ESC键返回菜单";for(;;){if(_getch()==27)break;}end:return true;}bool FamilySystem::demandMem(){cout<<"1.查询成员.\n\n";cout<<" 请输入姓名:";string na;try{istringstream sin(gets());sin>>na;}catch(int){goto end;}Member*m=seek(na);if(m!=nullptr)cout<<*m;else cout<<"\n\n 对不起,家谱中没有此人的信息!\n\n";cout<<"\n按ESC键返回菜单";for(;;){if(_getch()==27)break;}end:return true;}void FamilySystem::calculate(){//若家谱树为空,将各总体值置零if(root==nullptr){total=0;aveAge=0;aveHeight=0;aveMember=0;ratio=0;return;}//若root不为空,继续vector<Member*>genely;//存储这一代的指针genely.push_back(root);vector<Member*>next;//存储下一代的指针int girl=0;//女性数int family=0;//家庭数total=0;aveAge=0;aveHeight=0;//initializefor(;;){//循环叠加各所需总体数据//在这一代中for(auto p:genely){++total;aveAge+=p->age;aveHeight+=p->height;if(p->sex=="female"||p->sex=="女")++girl;}//判断下一代是否为空,若为空,跳到最后int jubge=0;for(auto p:genely){if(p->pson!=nullptr){++jubge;++family;}}if(jubge==0)goto cal;//找到下一代for(auto p:genely){Member*temp=p->pson;if(temp==nullptr)continue;else{next.push_back(temp);while(temp->pbro!=nullptr){next.push_back(temp->pbro);temp=temp->pbro;}}}genely=next;//迭进下一代next.clear();//清空next}//计算最后结果cal:total=total;aveAge/=total;aveHeight/=total;if(family==0)family=1;aveMember=static_cast<float>(total)/ static_cast<float>(family);if(girl!=0)ratio=static_cast<float>(total-girl)/ static_cast<float>(girl);else ratio=0;//退出return;}bool FamilySystem::demandAve(){calculate();cout<<"2.查询家庭整体情况.\n";cout<<endl<<endl;cout<<setw(30)<<"家庭的整体情况如下:\t" <<endl<<endl<<endl;cout<<setw(30)<<"总人数:\t"<<total<< endl<<endl<<endl;cout<<setw(30)<<"平均年龄:\t"<<aveAge <<endl<<endl<<endl;cout<<setw(30)<<"平均身高:\t"<< aveHeight<<endl<<endl<<endl;cout<<setw(30)<<"家庭平均人数:\t"<< aveMember<<endl<<endl<<endl;cout<<setw(30)<<"男女比例:\t"<<ratio;if(ratio!=0)cout<<" : 1"<<endl;else cout<<endl;cout<<"\n\n按ESC键返回菜单";for(;;){if(_getch()==27)break;}return true;}bool FamilySystem::modifyMem(){cout<<"5.修改成员信息.\n\n";cout<<" 请输入姓名:";string na;try{istringstream sin(gets());sin>>na;}catch(int){goto rend;}Member*temp=seek(na);if(temp==nullptr){cout<<"\n\n 家谱中没有此人的信息!\n";goto end;}cout<<"\n 选择修改项:\n\n";cout<<" 1.姓名 2.出生地点 3.生日\n\n";cout<<" 4.性别 5.身高6.年龄\n\n";cout<<" 7.学历 8.工作\n\n";cout<<" 按ESC键退出修改\n\n";for(;;){int get=_getch();if(get==27)break;switch(get){case49:cout<<"\n 姓名原为:"<<temp->name;cout<<"\n 请输入更改:";cin>> temp->name;if(temp->pson==nullptr)break;else{string na=temp->name;temp=temp->pson;temp->father=na;while(temp->pbro!=nullptr){temp=temp->pbro;temp->father=na;}break;}case50:cout<<"\n 出生地点原为:"<< temp->birthPlace;cout<<"\n 请输入更改:";cin>> temp->birthDay;break;case51:cout<<"\n 生日原为:"<<temp->birthDay;cout<<"\n 请输入更改:";cin>> temp->birthDay;break;case52:cout<<"\n 性别原为:"<<temp->sex;cout<<"\n 请输入更改:";cin>>temp->sex;while(temp->sex!="male"&&temp->sex!= "female"&&temp->sex!="男"&&temp->sex!="女"){cout<<"输入错误,性别必须为male、female、男或女!\n请重新输入:";cin>>temp->sex;}break;case53:cout<<"\n 身高原为:"<<temp->height;cout<<"\n 请输入更改:";cin>> temp->height;break;case54:cout<<"\n 年龄原为:"<<temp->age;cout<<"\n 请输入更改:";cin>> temp->age;break;case55:cout<<"\n 学历原为:"<<temp->education;cout<<"\n 请输入更改:";cin>> temp->education;break;case56:cout<<"\n 工作原为:"<<temp->job;cout<<"\n 请输入更改:";cin>> temp->job;break;default:;}cout<<" 修改成功!";}end:cout<<"\n\n按ESC键返回菜单";for(;;){if(_getch()==27)break;}rend:return true;}void FamilySystem::display(Member*r,int n){ string blank="";for(int i=0;i!=n;++i)blank+=" ";cout<<blank<<r->name<<endl;if(r->pson!=nullptr)display(r->pson,n+6);if(r->pbro!=nullptr)display(r->pbro,n);return;}bool FamilySystem::showFamily(){cout<<"6.显示整个家谱.\n\n";if(root==nullptr)cout<<"\n\n 家谱为空!\n\n";else display(root,0);//escapecout<<"\n\n按ESC键返回菜单";for(;;){if(_getch()==27)break;}return true;}bool FamilySystem::saveFile(){ofstream outfile;//打开保存家庭信息的文件,并写入信息outfile.open("Wholes.txt",ios::out);if(!outfile){return false;}calculate();outfile<<total<<" "<<aveAge<<" "<<aveHeight<<" "<<aveMember<<" "<< ratio;outfile.close();//打开保存家庭成员信息的文件并写入信息outfile.open("Members.txt",ios::out);if(!outfile){return false;}vector<Member*>genely;//保存一代成员的指针genely.push_back(root);while(1){//输出这一代成员的信息for(auto p:genely){if(p!=nullptr){outfile<<p->name<<" "<<p->birthPlace<<" "<<p->birthDay<<" "<<p->sex<<" "<<p->height<<" "<<p->age<<" "<<p->job<<" "<<p->education<< " "<<p->father<<endl;}}//计算这一代成员的儿女的人数//若这一代都没有儿女,则退出int jubge=0;for(auto p:genely){if(p!=nullptr){if(p->pson!=nullptr)++jubge;}}if(jubge==0)break;//找到下一代的成员,即这一代的儿女vector<Member*>next;for(auto p:genely){Member*temp=p->pson;if(temp!=nullptr){next.push_back(temp);while(temp->pbro!=nullptr){next.push_back(temp->pbro);temp=temp->pbro;}}}genely=next;//迭进下一代next.clear();}return true;}主函数及欢迎界面的实现#include<time.h>#include"SystemClass.h"using std::cout;using std::endl;using std::flush;int main(){//显示系统欢迎界面void welcome();system("color 02");//将系统背景颜色置为黑色,前景暗绿色welcome();//画面停留3秒const time_t start=time(0);while(1){time_t current=time(0);if(current-start==3)break;}FamilySystem genealogy;//程序唯一的一个家谱对象//从文件中读取出家庭成员的信息,若读取不成功,则强制退出程序if(!genealogy.fileInit()){system("cls");//输出错误信息for(int i=0;i!=10;++i)cout<<"\n";string str=" ";cout<<str<<"读取文件时发生错误,程序将终止!\n\n"<<str<<"即将强制退出···"<<flush;//5秒后退出const time_t start=time(0);while(1){time_t current=time(0);if(current-start==5)exit(1);//异常退出}}//程序核心功能while(1){system("cls");//清屏system("color 02");int n=genealogy.menu();//显示菜单if(n==27)break;//按ESC键退出switch(n){case49:system("cls");genealogy.demandMem();break;//选择菜单项1case50:system("cls");genealogy.demandAve();break;//选择菜单项2 case51:system("cls");genealogy.addMem(); break;//选择菜单项2case52:system("cls");genealogy.deleteMem();break;//选择菜单项3 case53:system("cls");genealogy.modifyMem();break;//选择菜单项5 case54:system("cls");genealogy.showFamily();break;//选择菜单项6 default:;}}//保存信息到文件中,若发生错误,则当前所做修改不保存并强制退出程序if(!genealogy.saveFile()){system("cls");//输出错误信息for(int i=0;i!=12;++i)cout<<"\n";cout<<"保存文件时发生错误,所有工作将丢失!\n即将强制退出···"<<flush;//5秒后退出const time_t start=time(0);while(1){time_t current=time(0);if(current-start==5)exit(1);//异常退出}}//显示即将退出cout<<"\n\n 3秒后退出系统···";const time_t end=time(0);while(1){time_t current=time(0);if(current-end==3)break;}//正常退出return0;}void welcome(){//显示欢迎界面char*wel[]={"***********************************************************************\n","* *\n","* *\n","**\n","* ******** * * **** * ** ** *\n","* * * * * * * ** * * * *\n","* * * * * * ** * * * *\n","* ******* ******* * * ** * * *\n","* * * * * ** * * *\n","* * * * ** * * * *\n","* * * * ** *** ****** *** *\n","* *\n","* ******** ** ** ******** *************** * * *\n","* * * * * * * ** * * * * *\n","* * * * * ** * * * * *\n","* ******** * ******** ******** * * * *\n","* * * * ** * * * *\n","* * * * ** * * *\n","* ******** *** ******** ********** * * *\n","* *\n","* *\n","* *\n","***********************************************************************\n"};cout<<endl;for(const auto&i:wel)cout<<" "<<i;return;}。