前几天看到CFAN论坛某位同学的作业帖子,决定帮一下,发帖子也是原先的版主,不理人家多不好。
拿到题目一看,是C++的,那位同学给了个源码,是论坛上另外一个同学写的,结构比较混乱,功能也没完善。
他原本的意思是让我修改下,结果我看那个结构,只能重构了。。这个很惭愧的说,不会C++,但是又答应帮人家了,于是拿出勇气来,床头放本Cpp primer,google准备着,vim开开。今天一整天的时间终于搞定了。
第一次写用了对象的C++程序,很多地方没有设置检查,溢出漏洞多多,虽然使用了类,但看起来很是面向过程的。毕竟第一次留个纪念。
更新信息:感谢徐老师的意见,于是决定把搜索那块重新写下,新建了一个搜索模板函数searchframe(),想查找什么内容,怎么比较,查询后是否修改可以由参数指定,增强了代码的复用性。而且可以为以后添加更多的查询/修改功能提供了条件。不过写的水平不怎么高,代码有点晦涩 。。
————————————————————————————————————————————————————————
题目要求:
设计一个类CStudent,类中包含一个学生的基本数据如下:
编号,姓名,性别,年龄,数学成绩,计算机成绩,外语成绩。 并假设编号为整数,且从1号往后连续编码;姓名为字符串,性别为字符。
如: 1 LiPing m 18 89 98 94
请采用binary文件形式,并使用随机读写处理方式,对自定义CStudent类的对象数据进行存储与读写处理(即是说,总按具有连续编码的编号num为“序”来对文件中的各对象数据进行随机读写处理)。并设计该类的成员函数,而且对输出运算符“<<”进行重载,使该运算符能够完成将一个学生的信息输出到屏幕上。
要求成员函数完成以下功能:
(1) 从键盘输入一个学生的有关信息,并将它们存入到数据文件中(按编号来确定写出位置)。
(2) 按编号对学生信息进行检索并将检索结果显示在屏幕上。
(3) 按姓名对学生信息进行检索并将检索结果显示在屏幕上。
(4) 计算某编号学生的总成绩与平均成绩。
(5) 列出所有总成绩超过n分的性别为s同学的有关信息(n,s由用户从键盘输入)。
- /****************************************
- *名称: student.cpp *
- *描述: 学生管理程序 *
- *功能: 添加,修改,按条件查询学生信息 *
- *环境: Fedora Linux 11 & GCC & x86 *
- *备注: davelv第一次Class于2010-01-10 *
- *更新: 新建了可复用的搜索模板searchframe *
- ****************************************/
- #include <iostream>
- #include <cstring>
- #include <vector>
- #include <fstream>
- #include <cstdlib>
- using namespace std;
- #define CIN_LEN 1024//缓冲区长度
- #define FILENAME "data"//数据文件名
- /////////////////////////////////////
- // 结构和类 //
- ///////////////////////////////////
- struct data//学生个人信息
- {
- int id;//学号
- char name[20];//名字
- char major[20];//专业
- char sex;//性别
- double ch,en,ma;//成绩
- int grade;//年级
- };
- class CStudent
- {
- protected:
- bool altered;//是否修改
- data info;//学生信息
- public:
- static int nowid ;//新学生自增id
- static void displayhead();//显示表头
- static void displayshorthead();//显示短表头
- CStudent();//构造
- void displayinfo();//显示全部学生信息
- void displayshortinfo();//显示学生短信息
- double getsum();//取总成绩
- double getave();//取得平均分
- double getch();//取语文成绩
- double geten();//取外语成绩
- double getma();//取数学成绩
- int set(bool isnew);//设置学生信息
- int getgrade();//取年级
- int getid();//取学号
- bool isaltered();//取是否修改
- char getsex();//取性别
- char* getname();//取姓名
- char* getmajor();//取专业
- data* getinfo();//取学生全部信息
- //定义友元函数以便重载运算符
- friend ostream& operator<<(ostream&,const CStudent&);
- friend istream& operator>>(istream&,CStudent&);
- };
- int CStudent::nowid = 1;//初始化类静态成员
- CStudent::CStudent()//基类构造
- {
- info.id=CStudent::nowid++;//子增id
- strcpy(info.name,"None");//名字
- info.ch=0;//语文成绩
- info.en=0;//外语成绩
- info.ma=0;//数学成绩
- info.grade=1;//年级
- altered=false;//未被修改
- }
- int CStudent::getgrade()
- {
- return info.grade;
- }
- double CStudent::getsum()
- {
- return info.ch+info.en+info.ma;
- }
- double CStudent::getave()
- {
- return (info.ch+info.en+info.ma)/3;
- }
- double CStudent::getch()
- {
- return info.ch;
- }
- double CStudent::geten()
- {
- return info.en;
- }
- double CStudent::getma()
- {
- return info.ma;
- }
- int CStudent::getid()
- {
- return info.id;
- }
- char CStudent::getsex()
- {
- return info.sex;
- }
- char * CStudent::getname()
- {
- return info.name;
- }
- bool CStudent::isaltered()
- {
- return altered;
- }
- data *CStudent::getinfo()
- {
- return &info;
- }
- void CStudent::displayinfo()
- {
- cout<<*this<<"/t"<<getsum()<<"/t"<<getave()<<endl;//利用重载运算符输出
- }
- void CStudent::displayshortinfo()
- {
- cout << *this<<endl;
- }
- void CStudent::displayhead()
- {
- cout<<"/n/t学号/t姓名/t性别/t专业/t年级/t中文/t英文/t数学/t总分/t平均分/n";
- }
- void CStudent::displayshorthead()
- {
- cout<<"/n/t学号/t姓名/t性别/t专业/t年级/t中文/t英文/t数学/n";
- }
- int CStudent::set(bool isalter)
- {
- cout<<"输入学生信息:/n";
- displayshorthead();
- if (isalter)
- displayshortinfo();
- cout<<"/t"<<info.id<<"/t";
- cin.clear();
- cin>> *this;//从标准输入获取学生信息
- altered=true;//已修改
- if(cin.fail())
- {
- cout<<"录入失败/n";
- cin.clear();
- cin.ignore(CIN_LEN,'/n');//这两行是用来清空输入缓冲
- return -1;
- }
- else
- {
- cout<<"录入成功/n";
- return 1;
- }
- }
- //重载输出符
- ostream &operator<<(ostream& out,const CStudent &right)
- {
- //输出学生的全部信息
- out <<"/t"<<right.info.id<<"/t"<<right.info.name<<"/t"<<right.info.sex<<"/t"
- <<right.info.major<<"/t"<<right.info.grade<<"/t"<<right.info.ch<<"/t"
- <<right.info.en<<"/t"<<right.info.ma;
- return out;
- }
- //重载输入符
- istream& operator>>(istream& in,CStudent& right)
- {
- //输入除ID外的其他信息
- in >>right.info.name>>right.info.sex>>right.info.major
- >>right.info.grade>>right.info.ch>>right.info.en>>right.info.ma;
- return in;
- }
- /////////////////////////////////
- // 初始化函数 //
- ////////////////////////////////
- int initial(vector<CStudent*> &stu)
- {
- fstream file;//输入文件
- CStudent *p;
- file.open(FILENAME, fstream::in|fstream::binary);//二进制输入打开
- if (!file)//文件是否打开成功
- return -1;
- while( file.peek()!=EOF )//是否到文件末尾
- {
- p=new CStudent();//新建一个学生对象
- file.read((char*) p->getinfo(),sizeof(data));//读入学生对象
- if(file.fail())//检查读入是否失败
- return -2;
- stu.push_back(p);//对象加入vector
- }
- if(!stu.empty())//如果从文件读入了对象
- CStudent::nowid = stu.back()->getid()+1;//则自增id设置为最后一个学生id+1
- file.close();//关闭
- return stu.size();//返回对象个数
- }
- ////////////////////////////////
- // 信息增加函数 //
- ///////////////////////////////
- void insert(vector<CStudent*> &stu)
- {
- char c='y';//输入控制字符
- int flag = 1;//标志位,1表示新增成功
- CStudent *p=new CStudent();
- while(c != 'n')//是否继续新增
- {
- flag = p->set(false);//设置学生信息
- if( flag == 1 )//如果设置成功
- {
- stu.push_back(p);//对象加入vector
- p = new CStudent();//新建下一个对象
- }
- cout << "是否继续添加学生(any/n)?";
- cin.clear();
- cin.ignore(CIN_LEN,'/n');
- cin.get(c);
- }
- //删除最后一个新建的对象,因为没有使用它
- delete p;
- CStudent::nowid--;
- }
- ///////////////////////////////
- // 查询全部信息函数 //
- /////////////////////////////
- int comparebynone(const void *a, const void *b )
- {
- return 0;
- }
- ////////////////////////////////
- // 按学号比较函数 //
- //////////////////////////////
- int comparebyid(const void *a, const void *b )
- {
- return *(const int *)a - ((CStudent *)(b))->getid();
- }
- ///////////////////////////////
- // 按姓名比较函数 //
- //////////////////////////////
- int comparebyname(const void *a, const void *b )
- {
- return strcmp((const char *)a, (const char *)(((CStudent *)b)->getname()));
- }
- ////////////////////////////////
- // 按年级比较函数 //
- //////////////////////////////
- int comparebygrade(const void *a, const void *b)
- {
- return (*(const int *)a - ((CStudent *)b)->getgrade());
- }
- /////////////////////////////////////
- // 按总分和性别比较函数 //
- ///////////////////////////////////
- int comparebymarkandsex(const void *a, const void *b)
- {
- double mark;
- char sex;
- sscanf((const char*)a,"%lf%c",&mark,&sex);
- return !(
- ( ((CStudent*)b)->getsum() >= mark )
- && ( (sex =='n') || (sex == ((CStudent*)b)->getsex()) )
- );
- }
- ///////////////////////////////
- // 搜索模板 //
- /////////////////////////////
- template <typename T>
- void searchframe(const char *info, T &condition ,vector<CStudent *> &stu, int (*compare)(const void *a, const void *b) ,bool isalter)
- {
- char c='y';
- int flag;
- while(c != 'n')
- {
- cin.clear();
- if(info != NULL)
- {
- cout<< "输入"<<info<<":";
- //cin.ignore(CIN_LEN,'/n');
- cin>>condition;
- }
- if(cin.fail())
- {
- cout << "输入错误/n";
- }
- else
- {
- //遍历vector查找
- for(vector<CStudent*>::size_type ix =flag=0; ix!=stu.size(); ++ix)
- { //判断是name是否相等
- if(compare(&condition,stu[ix]) == 0)
- {
- if( isalter )
- stu[ix]->set(isalter);
- else
- {
- if(flag == 0)
- CStudent::displayhead();
- stu[ix]->displayinfo();
- }
- flag = 1;
- }
- }
- if (flag == 0)//没有查到
- {
- cout<<"没有";
- if(info == NULL)
- cout<<"符合条件";
- else
- cout<<info<<"为"<<condition;
- cout<<"的学生/n";
- }
- }
- cout << "是否继续(any/n)?";
- cin.clear();
- cin.ignore(CIN_LEN,'/n');
- cin.get(c);
- }
- }
- ////////////////////////////////
- // 信息检索函数 //
- //////////////////////////////
- void fetch(vector<CStudent*> &stu)
- {
- int choose,id,grade;
- char name[20],markandsex[20];
- while (true)
- {
- cout << "/n/t1.显示全部学生信息/n"
- "/t2.按学号查学生信息/n"
- "/t3.按姓名查学生信息/n"
- "/t4.按年级查学生信息/n"
- "/t5.按成绩和性别查询/n"
- "/t6.返回上级菜单/n/n";
- lchoose:
- cout<<"输入您的选择:";
- choose=0;
- cin>>choose;
- switch(choose)
- {
- case 1 :searchframe(NULL,choose,stu,comparebynone,false); break;
- case 2 :searchframe("学号",id,stu,comparebyid,false); break;
- case 3 :searchframe("姓名",name,stu,comparebyname,false); break;
- case 4 :searchframe("年级",grade,stu,comparebygrade,false); break;
- case 5 :searchframe("分数和性别",markandsex,stu,comparebymarkandsex,false); break;
- case 6 :return ;
- default: cout<<"输入有误/n";cin.clear();cin.ignore(CIN_LEN,'/n');goto lchoose;
- }
- }
- }
- ////////////////////////////////
- // 信息保存函数 //
- //////////////////////////////
- int save(vector<CStudent*> &stu)
- {
- fstream file;
- file.open(FILENAME, fstream::out|fstream::binary);//二进制写打开文件
- if (!file)//判断打开是否成功
- return -1;
- //遍历全部对象
- for(vector<CStudent*>::size_type ix =0; ix!=stu.size(); ++ix)
- { //判断当前对象是否已修改
- if(stu[ix]->isaltered())
- { //修改了则写入文件
- file.seekp(ix*sizeof(data));
- file.write((char*) stu[ix]->getinfo(),sizeof(data));
- //写入是否成功
- if(file.fail())
- return -2;
- }
- }
- file.close();
- return 0;
- }
- ///////////////////////////////
- // 主函数 //
- //////////////////////////////
- int main()
- {
- vector<CStudent *> stu ;
- system("clear");//清屏
- //读入数据文件
- if(initial(stu)<0)
- {
- cout<<"初始化失败,请检查数据文件/""<<FILENAME<<"/"是否完好/n";
- return -1;
- }
- int choose;
- //主菜单循环
- while(true)
- {
- cout << "/n *----------学生信息管理系统----------*/n/n"
- " 1.录入信息/n"
- " 2.修改信息/n"
- " 3.检索学生/n"
- " 4.保存数据/n"
- " 5.保存退出/n"
- " 6.不保存退出/n/n"
- " *---------modify by davelv-----------*/n";
- lchoose:
- cout<<"输入您的选择:";
- choose=0;
- cin>>choose;
- switch(choose)
- {
- case 1 :insert(stu);break;
- case 2 :searchframe("学号",choose,stu,comparebyid,true);break;
- case 3 :fetch(stu);break;
- case 4 :if(save(stu) <0 )
- {
- cout<<"保存失败,请检查数据文件/""<<FILENAME<<"/"是否完好/n";
- };break;
- case 5 :if(save(stu) <0 )
- {
- cout<<"保存失败,请检查数据文件/""<<FILENAME<<"/"是否完好/n";
- }return 0;
- case 6 :return 0;
- default:cout<<"输入有误/n";cin.clear();cin.ignore(CIN_LEN,'/n');goto lchoose;
- }
- }
- return 0;
- }