一、设计思路
先写出数据类,再写出管理员操作类,首次通过管理员来添加更改数据信息,在这里要注意数据信息用文件保存,以便下次使用;然后写出客户端操作类,可以进行查询图书,借阅图书等操作;最后补充上账户登陆类,实现同一界面、不同操作。
二、具体实现
(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;
        }
    }
};

三、设计体会与小结
我在这次课程设计中,遇到的最多最大的问题是文件读写问题,总是在程序中运行的很好,但是文件无法写入,主要原因有输入输出运算符重载(运行时输入数据与重载数据类型不一致),基类析构函数(在基类析构函数调用写文件函数,而创建派生类对象,在最后保存数据是被基类数据覆盖),但恰恰是这些问题让我对文件读写这个新知识有了更深的理解。
建索引也是这次课程设计习得的,挺好用的,以后要继续尝试使用。
对于继承和多态,我是尝试着用的,还没发现问题,但是我还是觉得自己应该多加学习。

四、代码实现

图书馆管理系统