一、设计思路
先写出数据类,再写出管理员操作类,首次通过管理员来添加更改数据信息,在这里要注意数据信息用文件保存,以便下次使用;然后写出客户端操作类,可以进行查询图书,借阅图书等操作;最后补充上账户登陆类,实现同一界面、不同操作。
二、具体实现
(1)先考虑该系统所需要的数据类:
1>时间类(年月日,重载“<”用于比较时间前后);
2>记录类(学号,书号,借还时间,是否续借);
3>用户信息类(姓名,密码,类型,当前借阅,最大借阅,是否违规);
4>图书信息类(书号,书名,作者是否续借,总册数,可借本数);
※在用户信息类和图书信息类中都有储存记录的vector以及两个索引:在一本书借阅记录中查找借阅人,在用户借阅记录中查找借阅书;*在所有的数据类中都有输入输出运算符重载,方便整体输入输出。
istream&operator>>(istream&in,User&u)
{
in>>u.no;
if(u.no==-1)
return in;
in>>u.code>>u.type;
if(!u.type)
return in;
in>>u.name>>u.borrow>>u.biggest>>u.violate>>u.num;
return in;
}
ostream&operator<<(ostream&out,User&u)
{
out<<u.no<<" "<<u.code<<" "<<u.type;
if(!u.type)
return out;
out<<" "<<u.name<<" "<<u.borrow<<" "<<u.biggest<<" "<<u.violate<<" "<<u.num;
if(u.getNum())
for(int i=0; i<u.getNum(); i++)
out<<endl<<u.r[i];
return out;
}
(2)再分析该系统所需要的操作类:
1>管理员操作类(储存用户的vector和储存图书的vector以及两个查找指定用户或图书的索引)
增删查改用户和图书:先输入学号或书号,判断当前输入是否有效
(-1为无效结束输入),再通过查找判断输入的学号或书号是否存在,然后再进行后续操作。
增加:
void Administrator::addStu()
{
User u1;
int i;
cout<<“请添加用户信息,-1结束添加<<endl;
while(cin>>u1)
{
if(u1.getNo()==-1)
break;
u.push_back(u1);
i=u.size();
mm.insert(make_pair(u1.getNo(),i-1));
}
}
删除:
void Administrator::deleStu()
{
int no;
while(1)
{
cin>>no;
if(no==-1)
break;
int i=searchStu(no);
if(i!=-1)
{
it=u.begin()+i;
mm.erase(mm.find(no));
u.erase(it);
}
else
cout<<"输入学号有误,请重新输入"<<endl;
}
}
查询:
void Administrator::queryStu()
{
int no;
while(1)
{
cin>>no;
if(no==-1)
break;
int i=searchStu(no);
if(i!=-1)
cout<<u[i];
else
cout<<"输入学号有误,请重新输入"<<endl;
}
}
修改:
void Administrator::changeStu()
{
User u1;
int no;
while(1)
{
cin>>no;
if(no==-1)
break;
int i=searchStu(no);
if(i!=-1)
{
cin>>u1;
u[i]=u1;
}
else
cout<<"输入学号有误,请重新输入"<<endl;
}
}
文件操作:通过写文件将数据信息保存,以便下次使用;通过读文件将储存在文件的数据信息读入到程序中。
写文件:
void BaseData::saveStu()
{
ofstream outfile("20171820stu.txt",ios::out);
if(!outfile)
return ;
for(it=u.begin(); it!=u.end(); it++)
{
outfile<<*it;
outfile<<endl;
}
outfile.close();
}
读文件:
void BaseData::loadStu()
{
User U;
Record r;
int i=0;
ifstream infile("20171820stu.txt",ios::in);
if(!infile)
return;
u.clear();
mm.clear();
U.mmClear();
while(infile>>U)
{
if(U.getType()&&U.getNum())
for(int i=0; i<U.getNum(); i++)
{
infile>>r;
U.addRecord(r);
U.setMM(r,i);
}
u.push_back(U);
U.recordClear();
mm.insert(make_pair(U.getNo(),i));
i++;
}
infile.close();
}
2>客户端类(指定用户,储存图书的vector,三个索引)
查询图书,分为按书名查询和按作者查询,两者功能相似,现就其一说明——使用find_if函数对vector中储存的图书进行查找,符合条件的输出到屏幕;为了设定查询条件,在之前写了一个结构体
struct cmp1
{
string str;
cmp1(string s)
{
str = s;
}
bool operator()(Book&b)
{
return(b.getName().find(str)!=string::npos);
}
};
查询:
void Client::searchName(string s)
{
it1 = find_if(b.begin(),b.end(),cmp1(s));
if(it1!=b.end())
while(it1!=b.end())
{
cout<<b[it1-b.begin()]<<endl;
it1 = find_if(it1+1,b.end(),cmp1(s));
}
else cout<<"无此图书!"<<endl;}
借阅是通过输入书号,对其进行查询,若馆藏有此书,且判断用户是否违纪,此书是否有余,用户是否到达借书上限,进一步确认借书,
借书成功后,会自动生成借阅记录,在程序运行结束后,会保存在图书文件和用户文件中。
借阅:
void Client::borrow()
{
int s,sh,i;
cout<<"请输入借阅本数"<<endl;
cin>>s;
while(s)
{
cout<<"请输入所借书书号"<<endl;
cin>>sh;
i=searchBook(sh);
if(!u.getViolate())
{
if(u.getBorrow()<u.getBiggest())
{
if(i!=-1)
{
if(b[i].getRest())
{
b[i].setRest(b[i].getRest()-1);
u.setBorrow(u.getBorrow()+1);
Record r(u.getNo(),sh,t);
b[i].setNum(b[i].getNum()+1);
u.setNum(u.getNum()+1);
b[i].addRecord(r);
u.addRecord(r);
--s;
//b[i].displayRecord();
//u.displayRecord();
}
else
cout<<"该书已借出"<<endl;
}
else
cout<<"无此图书"<<endl;
}
else
{
cout<<"借阅数已达上限,无法继续借书"<<endl;
break;
}
}
else
{
cout<<"有违纪行为,无法借书"<<endl;
break;
}
}
}
还书是查找到书籍后,修改用户当前借阅,图书剩余本数
还书:
void Client::back()
{
int sh,s,i;
cout<<"请输入还书本数"<<endl;
cin>>s;
while(s)
{
cout<<"请输入所还书书号"<<endl;
cin>>sh;
i=searchBook(sh);
if(i!=-1)
{
b[i].setRest(b[i].getRest()+1);
u.setBorrow(u.getBorrow()-1);
--s;
}
else
cout<<"输入书号有误,请重新输入"<<endl;
}
}
续借是查找到图书,设置图书为续借
续借:
void Client::renew()
{
int sh,s;
cout<<"请输入续借本数"<<endl;
cin>>s;
while(s)
{
cout<<"请输入所续借书号"<<endl;
cin>>sh;
int i=searchBook(sh);
if(i!=-1)
{
b[i].modify(u.getNo());
u.modify(sh);
--s;
}
else cout<<"无此图书"<<endl;
}
}
3>登录类
使用虚函数编写,在它的基类中写一个空的登录函数,其功能在派生类中实现,
输入账号密码,判断账号是否存在,进一步判断密码是否正确,最后根据用户类型进入不同界面,实现不同操作。
登录:
class CheckIn:public BaseData
{
public:
void logIn()
{
int id,i;
string pw;
cout<<"请输入账号和密码"<<endl;
while(cin>>id>>pw)
{
i=searchStu(id);
if(i!=-1)
{
if(u[i].getCode()==pw)
{
if(u[i].getType())
{
cout<<"欢迎用户登陆"<<endl;
//u[i].setCode();
Time t;
cout<<"请输入当前时间"<<endl;
cin>>t;
Client c1(id,t);
//c1.searchName();
//c1.searchAuthor();
c1.borrow();
c1.back();
c1.renew();
break;
}
else
{
cout<<"欢迎管理员登陆"<<endl;
Administrator a;
a.addStu();
a.displayAllStu();
a.deleStu();
a.queryStu();
a.changeStu();
a.displayAllStu();
a.addBook();
a.displayAllBook();
a.deleBook();
a.queryBook();
a.changeBook();
a.displayAllBook();
break;
}
}
else
cout<<"密码错误,请重新账号和密码输入"<<endl;
}
else
cout<<"账号错误,请重新账号和密码输入"<<endl;
}
}
};
三、设计体会与小结
我在这次课程设计中,遇到的最多最大的问题是文件读写问题,总是在程序中运行的很好,但是文件无法写入,主要原因有输入输出运算符重载(运行时输入数据与重载数据类型不一致),基类析构函数(在基类析构函数调用写文件函数,而创建派生类对象,在最后保存数据是被基类数据覆盖),但恰恰是这些问题让我对文件读写这个新知识有了更深的理解。
建索引也是这次课程设计习得的,挺好用的,以后要继续尝试使用。
对于继承和多态,我是尝试着用的,还没发现问题,但是我还是觉得自己应该多加学习。
四、代码实现
图书馆管理系统