图书管理系统(C++)

注意事项:

在代码测试之前必须先建立文件并导入内容以供测试使用,系统没有提示内容,用起来可能感到十分晦涩难以理解(可直接参考源代码,源代码还是非常浅显易懂的),这里仅供学习参考,不追求功能完善与实用性,如有问题或错误敬请谅解,欢迎积极指出。

源代码:

//遇到的几个问题:

//先写最重要的问题:可以考虑使用继承,因为很多功能都是类似的,使用继承我觉得至少可以减少三分之一的代码量(待解决)(另外注释掉的调试的代码没有去掉,方便自己看的时候注意到该注意的问题)

//最该注意的问题:重载,重载的输入输出很严格,不能有丝毫差错,否则文件读取失败,程序相当于崩溃(已解决)

//对于虚删问题(map的删除与重建问题)其实觉得似乎每删除一个对象都要进行修改的话,当数据量过大时,反而效率不会太高吧
//所以使用的是使用时进行删除先虚删,然后当该次操作完成后保存文件时进行对虚删的处理,然后在保存与再读取时相当于进行了实删(这里只是针对管理端的删除书籍与删除用户操作)(已实现)

//对于模糊搜索的问题,原意应该是出现每个字符最多(最符合搜索内容的一项,但是这似乎要用到对字符串的处理(同学似乎是这样说的)(待处理,似乎也并不是好处理的)
//我用的是精确一点的查找,例如对书籍信息来说可以直接搜索书籍的书名,或者作者,出版社等信息进行搜索,重名等可以直接显示
//搜索完记下要搜索书籍的书号再精确进行对该书的具体查询

//对于借阅日期来说,其实可以将每个借阅日期转化为具体整数,然后通过两个整数之间的差值来进行判断是否超期,但是比较麻烦,在计算时间时如果使用int则需要对每组时间数据进行循环赋值,效率也并不高
//另外对于日期的处理似乎可以对日期进行加减号重载,然后我们可以得出两者的日期差来判断是否超期或何时归还然后直接显示的问题(对于超期判断的重载待处理)(但是我们在借书时如果书籍已经被借完了,我们可以通过借阅记录以及续借记录来进行具体推测出最早的书籍应何时归还)
//已通过重载加减号实现

//其余还有的问题就写在代码中了(不摘出来了)(大体的功能可以实现了,还有很多细节待优化)
//这里用的是管理端与用户端结合到一起的,没有摘出来分为管理端与用户端(其实复制粘贴过去应该就能实现两个代码),这里添加了登录情况,可以直接登录为管理端或客户端(当然这次代码里没有使用管理端(注释掉了,取消注释也可以直接用))


//对于书籍查询的继承,由于一开始写的是查询与修改写到了一起(管理端),用户端查询与借阅写到了一起,当然这里可以把查询列出来写一个大类,然后使用继承。
//另外对于文件的导入与保存,可以使用继承,至少在文件的保存上(调用save函数)都是一样的。(待处理)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<map>
#include<set>
#include<vector>
#include<string>
#include<fstream>
using namespace std;

class Data     //起初只是用来表示出版日期,后来发现借阅与归还日期都可用
{
   
    int year;
    int month;
    int day;
public:
    Data(){
   };
    Data(int year1,int month1,int day1):year(year1),month(month1),day(day1){
   };
    friend ostream&operator<<(ostream&os,const Data&d);
    friend istream&operator>>(istream&is,Data&d);
    bool operator<(const Data &d)const    //日期比较函数
    {
   return year!=d.year?year<d.year:month!=d.month?month<d.month:day<d.day;}
    int operator-(Data d)  //对日期的操作,可以直接按月份进行两个月的相减操作,也可以按如下方式进行精确一点的操作(还可以判断平年闰年进行更精确的操作)
	{
   
	    if(year<d.year||(year==d.year&&month<d.month)||(year==d.year&&month==d.month&&day<d.day))
            return -1;  //当日期没有办法相减(后面的日期大时)
        int m[13] = {
   0,31,28,31,30,31,30,31,31,30,31,30,31};  //按下标(对应月份)补齐,第一个数不计,对于每年的二月按28天计(不考虑平年闰年)
	    int count=0;
	    int a=d.year,b=d.month,c=d.day;
	    while(1)
	    {
   
            if(this->year==a&&this->month==b&&this->day==c) break;
	        if(c==m[b]){
   
                c=1;
                if(b==12)
                 {
   
                     b=1;
                     a++;
                 }
                 else b++;
	        }
	        else c++;
	        count++;
        }
	        return count;
	}
	Data operator+ (int x)
	{
   
        int m[13] = {
   0,31,28,31,30,31,30,31,31,30,31,30,31};  //按下标(对应月份)补齐,第一个数不计,对于每年的二月按28天计(不考虑平年闰年)
        while(x--)
        {
   
            if(day==m[month])
            {
   
                day=1;
                if(month==12)
                {
   
                    year++;
                    month=1;
                }
                else month++;
            }
            else day++;
        }
        return *this;
	}
};

istream&operator>>(istream&is,Data&d)    //先重载出版日期的输入输出
{
   
    while(1)
    {
   
        is>>d.year>>d.month>>d.day;
        if(d.year<1900||d.year>2019||d.month<1||d.month>12||d.day<1||d.day>31); //这里可以优化为判断闰年或平年然后进行判断
        else break;
    }
    return is;
}
ostream&operator<<(ostream&os,const Data&d)
{
   
    //os<<"year:"<<d.year<<" "<<"month:"<<d.month<<" "<<"day:"<<d.day;
    //为了输入输出相统一,改为下面的输出形式
    os<<d.year<<" "<<d.month<<" "<<d.day;
    return os;
}

class Book        //书籍信息
{
   
    string name;
    string writer;
    string number;
    string press;  //出版社
    Data data;  //出版日期
    int zongshu,kejie;
    int guancang;   //后来加的,图书馆会馆藏一些书籍(非外借),可以用该量来判断书籍是否外借
    string s_name,s_writer,s_number,s_press,s_data,s_zongshu,s_kejie,s_guancang;
public:
    Book(){
   };
    Book(string name1,string writer1,string number1,string press1,Data data1,int zongshu1,int kejie1):name(name1),writer(writer1),number(number1),press(press1),data(data1),zongshu(zongshu1),kejie(kejie1),guancang(zongshu1-kejie1){
   };
    void bookclear() {
   name="0"; writer="0"; number="0"; press="0"; zongshu=0; kejie=0; guancang=0;} //虚删
    string getPress() {
   return press;}
    Data getdata() {
   return data;}
    string getName() {
   return name;}
    string getWriter() {
   return writer;}
    string getNumber() {
   return number;}
    int getZongshu() {
   return zongshu;}
    int getKejie() {
   return kejie;}
    int getGuancang() {
   return guancang;}
    void setGuancang(int guancang1) {
   guancang=guancang1;}
    void setZongshu(int zongshu1) {
   zongshu=zongshu1;}
    void setKejie(int kejie1) {
   kejie=kejie1;}
    //类的整体输入输出(重载输入输出函数),不再构造打印函数了
    friend ostream&operator<<(ostream&os,const Book&b);    //书籍信息会变动(不能用const)(但是像出版信息就没有必要修改,直接const)
    friend istream&operator>>(istream&is,Book&b);
    bool operator==(const Book &b)const
    {
   
        return name!=b.name?0:writer!=b.writer?0:number!=b.number?0:1;        //其实只需判断number(书号唯一)
    }
};

istream&operator>>(istream&is,Book&b)     //重载书籍信息的输入输出
{
   
    is>>b.s_name>>b.name>>b.s_writer>>b.writer>>b.s_number>>b.number>>b.s_press>>b.press>>b.s_data>>b.data>>b.s_zongshu>>b.zongshu>>b.s_kejie>>b.kejie>>b.s_guancang>>b.guancang;
    //b.guancang=b.zongshu-b.kejie;
    return is;
}
ostream&operator<<(ostream&os,const Book&b)
{
   
    os<<"book.name: "<<b.name<<" "<<"writer: "<<b.writer<<" "<<"book.number: "<<b.number<<" "<<"book.press: "<<b.press<<" "<<"book.publishdata: "<<b.data<<" "<<"zongshu: "<<b.zongshu<<" "<<"kejie: "<<b.kejie<<" "<<"guancang.not.lend: "<<b.guancang<<endl;
    return os;
}

class Reader   //用户信息
{
   
    string name;
    string number;
    int jieyue;
    int kejie;
    int xujie_;
    string s_name,s_number,s_yijie,s_kejie,s_jieshuxinxi,s_xujie,s_lend,s_back;
    Book b1;
    Data d1,d2;
    vector<Book>reader;        //已借阅的书籍信息
    vector<Book>::iterator it;
    vector<Data>jieyuedata;
    vector<Data>backdata;  //当然这里可以直接使用map关联
    vector<Data>::iterator d;
    vector<int>xujie;   //对于每组借阅的书籍信息进行记录,只存0和1(续借过记为1,反之为0)
    //vector<int>::iterator ii; 对于续借这里直接也是通过下标解决了
public:
    Reader(){
   };
    Reader(string name1,string number1,int jieyue1):name(name1),number(number1),jieyue(0){
   };
    void clearReader() {
   reader.clear();}
    void clear() {
   name="0"; number="0";}
    string getName() {
   return name;}
    string getNumber() {
   return number;}
    int getJieyue() {
   return jieyue;}
    void setName(string name1) {
   name=name1;}
    void setNumber(string number1) {
   number=number1;}
    //这里需要对用户个人的借书记录进行增删操作(借书记录操作只有增删两步)
    //当一次借阅或归还好几本的时候,可以通过while(次数i--)进行i次循环调用函数进行操作
    void addjieyue(Book b,Data day) {
   jieyue++; reader.push_back(b); jieyuedata.push_back(day); Data day_=day+30; backdata.push_back(day_); xujie_=0; xujie.push_back(xujie_);}            //if(jieyue<10)在外部进行判断
    int backjieyue(Book b)      //if(jieyue>=10)在外部进行判断
    {
   
        //jieyue--;
        //首先要在自己借到的书中找到那本书,确保自己有该书后归还
        int i=0,j=0;
        int ii=0;      //表示输入的图书是否借阅过,能否进行归还(防止输入错误信息刷借阅量)
        for(it=reader.begin();it!=reader.end();it++)
        {
   
            //找到就还,找不到就无法还
            //{reader.erase(it); i=j; jieyue--; break;} //break一定要加,因为很有可能一个用户借了两本一样的书(代借)(这可能有反规定,如果需要避免这种情况可以在借书时加限制,但是图书馆似乎允许这种代借,只是名义上不准代借而已)
        //当使用迭代器时需要对赋值号等符号进行重载,另外Data数据在此代码中只是具体到天(如果按天查询删除会出现错误)(一天借了好几本书时)
        //试过使用map将书籍与借阅日期进行关联,但在输出时由于两个自定义类的输出使用迭代器赋值初始值时又会产生以上问题(并不太容易实现,总是产生与STL内部相冲突的问题)
        //试过使用一个类将书籍与借阅日期进行关联,但是类向量的删除也会存在以上问题
        //总之,重载符尽量还是少用比较好(因为上面的几种实验总是与STL内部相冲突。。。)
            if(*it==b){
   
            reader.erase(it);
            jieyue--;
            ii=1;
            i=j;
            /*for(int k=i;k<=getJieyue()-1;k++) { jieyuedata[k]=jieyuedata[k++]; xujie[k]=xujie[k++]; }*/ //下标法不易实现,直接查找删除更便捷
            jieyuedata.erase(jieyuedata.begin()+j);
            backdata.erase(backdata.begin()+j);
            xujie.erase(xujie.begin()+j);
            break;
           }
            j=j+1;
        }
        return ii;
    }
    int xujiejieyue(Book book,Data day)  //int判断是否续借成功(因为要返回写入续借记录,如果续借失败则没有记录,反之则应记录)
    {
   
        int ii=0;
        int i=0;
        for(it=reader.begin();it!=reader.end();it++)
        {
   
            if(*it==book)
            {
   
                if(xujie[i]==1)
                    cout<<"flase,yixujie"<<endl;
                else
                {
   
                    jieyuedata[i]=day;  //续借日期当天记为新借阅日期
                    backdata[i]=day+30;
                    xujie[i]++;
                    ii=1;
                }
                break;
            }
            i++;
        }
        return ii;
    }
    int chaoqi(Data d)
    {
   
        int count=0;
        for(int i=0;i<reader.size();i++)
        {
   
            if(d-jieyuedata[i]>30)   //d-backdata[i]>0
            {
   
                cout<<reader[i];
                count++;
            }
        }
        return count;
    }
    friend ostream&operator<<(ostream&os,const Reader&r);
    friend istream&operator>>(istream&is,Reader&r);
};
//对每一位用户保证输入输出相同,这样我们就可以完全从用户数据中读取与操作数据
//中间试图对用户输入信息正确性做判断(用了几个while(1)),但是每次操作完数据都是那样不会变的,因此默认为数据是正确的
istream&operator>>(istream&is,Reader&r)
{
   
    is>>r.s_name>>r.name>>r.s_number>>r.number>>r.s_yijie>>r.jieyue>>r.s_kejie>>r.kejie;
    r.reader.clear();
    r.jieyuedata.clear();
    r.backdata.clear();
    r.xujie.clear();
    if(r.jieyue!=0)
    {
   
        is>>r.s_jieshuxinxi;
        for(int i=0;i<r.jieyue;i++)
        {
   
            //如果在这里定义Book b1;Data d1;然后输入再导入的话不会成功的,只有定义在类内然后进行操作才可以
            is>>r.b1;
            r.reader.push_back(r.b1);
            is>>r.s_lend;
            is>>r.d1;
            is>>r.s_back;
            is>>r.d2;
            r.jieyuedata.push_back(r.d1);
            r.backdata.push_back(r.d2);
            is>>r.s_xujie;
            is>>r.xujie_;
            r.xujie.push_back(r.xujie_);
        }
    }
    return is;
}

ostream&operator<<(ostream&os,const Reader&r)
{
   
    os<<"name:"<<" "<<r.name<<" "<<"number:"<<" "<<r.number<<" "<<"yijie:"<<" "<<r.jieyue<<" "<<"kejie:"<<" "<<(10-r.jieyue)<<endl;
    if(r.reader.size()!=0)
    {
   
        os<<"jieshuxinxi:"<<endl;
        for(int i=0;i<r.jieyue;i++)       //这里尝试使用迭代器,但是使用迭代器似乎又要重载运算符“=”(赋值号)
            os<<r.reader[i]<<"lend:"<<" "<<r.jieyuedata[i]<<" "<<"back:"<<" "<<r.backdata[i]<<" "<<"xujie:"<<" "<<r.xujie[i]<<endl;
    }
    return os;
}

class Record  //总借阅记录
{
   
    //string bookname,writer,booknumber;
    Book book;  //直接用书籍的整体信息去替代部分信息
    string name,number;  //借阅记录在这里是不可见其他用户私人借书信息的(用户端)(当然管理端可通过查找用户来完全进行查看)
    Data data;     //表示借阅日期归还日期(还有续借日期)
    string fangshi; //借、还、续借
    //用户查询记录只是简单地查询所需书籍当下的记录(很久之前的记录,比如借了又换上的记录没有必要也没有权限查询
    //((管理端是有的)所以针对管理端,要利用multimap对每组记录进行排序或查询(通过用户查找,或通过书籍查找,或者按时间进行时间段查询等)
    //管理端该查询还待补充
    string name_,number_;
public:
    Record(){
   };
    Record(Book book1,string name1,string number1,Data data1,string fangshi1):book(book1),name(name1),number(number1),data(data1),fangshi(fangshi1) {
   };
    Book getBook() {
   return book;}
    string getBooknumber() {
   return book.getNumber();}
    string getName() {
   return name;}
    string getnumber() {
   return number;}
    Data getData() {
   return data;}
    string getFangshi() {
   return fangshi;}
    //记录没必要使用set函数
    //友元重载输入输出
    friend ostream&operator<<(ostream&os,const Record&rec);
    friend istream&operator>>(istream&is,Record&rec);
};

istream&operator>>(istream&is,Record&rec)
{
   
    while(is>>rec.fangshi)
    {
   
        if(rec.fangshi=="lend:"||rec.fangshi=="back:"||rec.fangshi=="xujie:")
            break;
    }
    //is>>rec.bookname>>rec.writer>>rec.booknumber>>rec.name>>rec.number>>rec.data;
    is>>rec.name_>>rec.name>>rec.number_>>rec.number>>rec.book>>rec.data;
    return is;
}

ostream&operator<<(ostream&os,const Record&rec)
{
   
    if(rec.fangshi=="lend:")
        os<<"lend:"<<" ";
        else if(rec.fangshi=="back:")
            os<<"back:"<<" ";
            else
                os<<"xujie:"<<" ";
    //os<<"bookname:"<<rec.bookname<<" "<<"writer:"<<rec.writer<<" "<<"booknumber:"<<rec.booknumber<<endl;
    os<<"username:"<<" "<<rec.name<<" "<<"usernumber:"<<" "<<rec.number<<endl;
    os<<rec.book;
    os<<rec.data<<endl;
    return os;
}

class Guanliop
{
   
    vector<Book>book;
    vector<Reader>user;
    vector<Record>record;
    multimap<string,int>bookname;
    multimap<string,int>writer;
    multimap<string,int>booknumber;
    multimap<string,int>press;
    multimap<Data,int>publishdata;

    multimap<string,int>name;
    multimap<string,int>number;
    //记录查询一般是按日期来查
    multimap<Data,int>data;
    /* multimap<string,int>bookname1; multimap<string,int>writer1; multimap<string,int>booknumber1; */
    multimap<string,int>name1;
    multimap<string,int>number1;

    multimap<string,int>::iterator m,m1,m2;
    multimap<Data,int>::iterator d,d1,d2;
public:
    Guanliop(){
   loadbook(),loaduser(),loadrecord();}
    //~Guanliop() {save();}
    ~Guanliop() {
   };
    //读取从三个文件依次读取(当某个文件读取失败时不会影响另外两个)
    //保存文件直接保存三个即可
    void loadbook();
    void loaduser();
    void loadrecord();
    void save();
    void addBook();
    void search();
    void setBook();   //对书籍的操作,首先要查找该书,其次进行修改或删除
    void openBook();  //书籍全览
    void addReader();
    void setReader();
    void openReader(); //用户全览
    void openRecord(); //记录只查不改
};

void Guanliop::loadbook()
{
   
    Book b;
    ifstream in("2018212591马俊光的书籍.txt",ios::in);
    if(!in)
    {
   
        cout<<"Can't load book!"<<endl;
        return;
    }
    book.clear();
    bookname.clear();
    writer.clear();
    booknumber.clear();
    press.clear();
    publishdata.clear();
    while(in>>b)   //与增添书籍信息代码一致(后来讲到的的复用似乎可以使用)
    {
   
        //读取与保存时无需考虑重复情况,因为增添时已经进行了操作
        book.push_back(b);
        bookname.insert(make_pair(b.getName(),book.size()-1));
        writer.insert(make_pair(b.getWriter(),book.size()-1));
        booknumber.insert(make_pair(b.getNumber(),book.size()-1));
        press.insert(make_pair(b.getPress(),book.size()-1));
        publishdata.insert(make_pair(b.getdata(),book.size()-1));
    }
    in.close();
};

void Guanliop::loaduser()
{
   
    Reader u;
    ifstream infile("2018212591马俊光的用户.txt",ios::in);
    if(!infile)
    {
   
        cout<<"Can't load user!"<<endl;
        return;
    }
    user.clear();
    name.clear();
    number.clear();
    while(infile>>u)
    {
   
        user.push_back(u);
        name.insert(make_pair(u.getName(),user.size()-1));
        number.insert(make_pair(u.getNumber(),user.size()-1));
    }
    infile.close();
}

void Guanliop::loadrecord()
{
   
    Record r;
    record.clear();
    data.clear();
    /* bookname1.clear(); writer1.clear(); booknumber1.clear(); */
    //管理端待操作
    name1.clear();
    number1.clear();
    ifstream inrec("2018212591马俊光的记录.txt",ios::in);
    {
   
        if(!inrec)
        {
   
            cout<<"Can't load record!"<<endl;
            return;
        }
    }
    inrec.close();
}

void Guanliop::save()
{
   

    ofstream outfile("2018212591马俊光的用户.txt",ios::out);
    if(!outfile)
    {
   
        cout<<"Save User false!";
        return;
    }
    for(int i=0;i<user.size();i++)
    {
   
        if(user[i].getNumber()=="0");
        else outfile<<user[i];
    }
    outfile.close();

    ofstream outbook("2018212591马俊光的书籍.txt",ios::out);
    if(!outbook)
    {
   
        cout<<"Save Book false!";
        return;
    }
    for(int i=0;i<book.size();i++)
    {
   
        if(book[i].getNumber()=="0");
        else outbook<<book[i];
    }
    outbook.close();
    return;
};

void Guanliop::addBook()
{
   
    Book b;
    cin>>b;
    m=booknumber.find(b.getNumber());      //看之前是否存在该书
    if(m!=booknumber.end())
    {
   
        int zongshu1=book[m->second].getZongshu()+b.getZongshu();
        int guancang1=book[m->second].getGuancang()+b.getGuancang();
        int kejie1=book[m->second].getKejie()+b.getKejie();
        book[m->second].setZongshu(zongshu1);
        book[m->second].setKejie(kejie1);
        book[m->second].setGuancang(guancang1);
    }
    else
    {
   
        book.push_back(b);
        bookname.insert(make_pair(b.getName(),book.size()-1));
        writer.insert(make_pair(b.getWriter(),book.size()-1));
        booknumber.insert(make_pair(b.getNumber(),book.size()-1));
        press.insert(make_pair(b.getPress(),book.size()-1));
        publishdata.insert(make_pair(b.getdata(),book.size()-1));
    }
}

void Guanliop::search()
{
   
    string in;
    cin>>in;
    int i=0;     //标记是否查到
    m=booknumber.find(in);
    if(m!=booknumber.end())
    {
   
        i=1;
        cout<<"Found"<<endl;
        cout<<book[m->second];
    }
    else
    {
   
        m=bookname.find(in);
        m1=bookname.lower_bound(in);
        m2=bookname.upper_bound(in);
        for(m=m1;m!=m2;m++)
        {
   
            i=1;
            cout<<book[m->second];
        }
        m=writer.find(in);
        m1=writer.lower_bound(in);
        m2=writer.upper_bound(in);
        for(m=m1;m!=m2;m++)
        {
   
            i=1;
            cout<<book[m->second];
        }
        m=press.find(in);
        m1=press.lower_bound(in);
        m2=press.upper_bound(in);
        for(m=m1;m!=m2;m++)
        {
   
            i=1;
            cout<<book[m->second];
        }
        //出版日期查询略去,因为一般是不会只记得出版日期的(这里输入也存在差异,不再对其进行处理)
        //这里又把日期查询显示出来:(当输入publishdata时按出版日期查询)
        //(在此之前in为publishdata,因此前面查不到也不输出任何信息)
        if(in=="publishdata")
        {
   
            Data data_;
            cin>>data_;
            d=publishdata.find(data_);
            d1=publishdata.lower_bound(data_);
            d2=publishdata.upper_bound(data_);
            for(d=d1;d!=d2;d++)
            {
   
                i=1;
                cout<<book[d->second];
            }
        }
    }
    if(i==0)
    cout<<"Not Found!"<<endl;
}

void Guanliop::setBook()
{
   
    //由于我们规定的书号是唯一的(每一种书一个书号),我们便可以利用书号对书籍进行查找修改与删除等操作
    string in;
    cin>>in;
    int i=0;     //标记是否查到
    m=booknumber.find(in);
    if(m!=booknumber.end())
    {
   
        i=1;
        cout<<book[m->second];
        string caozuo;
        while(cin>>caozuo)
        {
   
            if(caozuo=="clear"||caozuo=="delete"||caozuo=="return")
                break;
        }
        if(caozuo=="return");
        if(caozuo=="clear")  //清空书籍数据信息
        {
   
            if(book[m->second].getGuancang()!=(book[m->second].getZongshu()-book[m->second].getKejie()))  //此时有该种类型书目借出,不可清零
            cout<<"lend not back,clear false"<<endl;
            else
            {
   
                book[m->second].bookclear();       //暂时还没有删掉multimap里的数据m=name.find(s);
                //这里使用book[m->second]获取书籍信息后,可以对每个map使用查找并判断其关联值是否为i来进行删除
                booknumber.erase(m);
            }
        }
        if(caozuo=="delete") //删除几本或全部删除(但是保留书籍信息)或者更改可借与馆藏关系(这里只写到要删除几本书)
        {
   
            /* while(1) //本来是set函数,但是觉得书籍整体信息不会变 { cin>>caozuo; if(caouzo=="bookname") { cin>>caozuo; book[m->second].setName(caozuo); } if(caozuo=="writer") { cin>>caozuo; book[m->second].setWriter(writer); } } */
            int j;
            cin>>j;    //表示删除几本
            if(book[m->second].getKejie()>=j)
            {
   
                int k=book[m->second].getKejie()-j;
                int o=book[m->second].getZongshu()-j;
                book[m->second].setKejie(k);
                book[m->second].setZongshu(o);
            }
            else cout<<"not enough!"<<endl;
        }
    }
    if(i==0)
    cout<<"Not Found!"<<endl;
}

void Guanliop::addReader()
{
   
    Reader r;
    cin>>r;
    m=number.find(r.getNumber());
    if(m==number.end())
    {
   
        user.push_back(r);
        name.insert(make_pair(r.getName(),user.size()-1));
        number.insert(make_pair(r.getNumber(),user.size()-1));
    }
    else cout<<"user existed,add false"<<endl;
}

void Guanliop::setReader()
{
   
    //先查找用户
    string fangshi;
    cin>>fangshi;
    int i=0;
    m=number.find(fangshi);
    if(m!=number.end())
    {
   
        i=1;
        string s=user[m->second].getName();
        string ss=user[m->second].getNumber();
        int j=m->second;
        string caozuo;
        cout<<"Found"<<endl;
        cout<<user[m->second];
        while(1)
        {
   
            cin>>caozuo;
            if(caozuo=="clear"||caozuo=="set"||caozuo=="return")
                break;
        }
        if(caozuo=="return");
        if(caozuo=="set")
        {
   
            while(1){
   
            cin>>caozuo;
            if(caozuo=="name")
            {
   
                cin>>caozuo;
                //user[j].setName(caozuo); //一定不能先改名,否则map查找查的是新名,然后就给删除了。。。
                //name.insert(make_pair(caozuo,j));
                //cout<<user[j].getName()<<endl;
                m=name.find(user[j].getName());
                //if(m!=name.end()){ //该句其实没有必要,因为一定存在
                m1=name.lower_bound(user[j].getName());
                m2=name.upper_bound(user[j].getName());
                for(m=m1;m!=m2;m++)
                {
   
                    if(m->second==j)
                    {
   
                        name.erase(m);
                        break;  //不加break会导致程序出现问题。。。//其实无所谓了,发现不是这里的问题(不过加上也挺好的,不必接着遍历了,因为只需要删除那一个就好)
                    }
                }
                name.insert(make_pair(caozuo,j));
                user[j].setName(caozuo);
                /* for(m=name.begin();m!=name.end();m++) { cout<<m->first<<" "<<m->second<<endl; } */
                //}
            }
            //借阅号码是唯一,默认不可改(这里不做处理)
            if(caozuo=="end")
                break;
            }
        }
        if(caozuo=="clear")
        {
   
            user[j].clear();  //这里名字map已删除
            m=name.find(s);
            m1=name.lower_bound(s);
            m2=name.upper_bound(s);
            for(m=m1;m!=m2;m++)
            if(m->second==j)
            {
   
                 name.erase(m);
                 break;
            }
            m=number.find(ss);
            m1=number.lower_bound(ss);
            m2=number.upper_bound(ss);
            for(m=m1;m!=m2;m++)
            if(m->second==j)
            {
   
                 number.erase(m);
                 break;
            }
        }
    }
    else {
   
    m=name.find(fangshi);
    m1=name.lower_bound(fangshi);
    m2=name.upper_bound(fangshi);
    for(m=m1;m!=m2;m++)
    {
   
        i=1;
        cout<<user[m->second];
    }
    }
    if(i==0)
        cout<<"Not Found!"<<endl;
}

//以总览的方式全部显示书籍
void Guanliop::openBook()
{
   
    //按书名进行排序查看
    string fangshi;
    while(1)
    {
   
        cin>>fangshi;
        if(fangshi=="bookname"||fangshi=="writer"||fangshi=="booknumber"||fangshi=="press"||fangshi=="data")
            break;
    }
    if(fangshi=="bookname")
    for(m=bookname.begin();m!=bookname.end();m++)
    {
   
        cout<<book[m->second]<<endl;
    }
    if(fangshi=="writer")
    for(m=writer.begin();m!=writer.end();m++)
    {
   
        cout<<book[m->second]<<endl;
    }
    if(fangshi=="booknumber")
    for(m=booknumber.begin();m!=booknumber.end();m++)
    {
   
        cout<<book[m->second]<<endl;
    }
    if(fangshi=="press")
    for(m=press.begin();m!=press.end();m++)
    {
   
        cout<<book[m->second]<<endl;
    }
    if(fangshi=="data")
    for(d=publishdata.begin();d!=publishdata.end();d++)
    {
   
        cout<<book[d->second]<<endl;
    }
}
void Guanliop::openReader()
{
   
    string fangshi;
    while(1)
    {
   
        cin>>fangshi;
        if(fangshi=="name"||fangshi=="number")
            break;
    }
    if(fangshi=="name")
    for(m=name.begin();m!=name.end();m++)
        cout<<user[m->second];
    if(fangshi=="number")
    for(m=number.begin();m!=number.end();m++)
        cout<<user[m->second];
}

class Readerop:public Guanliop
{
   
    //用户端首先要找到自己,然后能查到所有书籍的相关信息
    vector<Reader>user;
   // vector<Reader>::iterator it;
    vector<Book>book;
    vector<Record>rec;  //要导入的总的记录
    vector<Record>rec1;  //要查询的书籍的记录(处理后的)
    multimap<string,int>bookname;
    multimap<string,int>writer;
    multimap<string,int>booknumber;
    multimap<string,int>press;
    multimap<Data,int>publishdata;
    multimap<string,int>recnumber;  //记录书号输出记录
    //其实也可以直接来定义<Bppk,int>的形式
    multimap<string,int>::iterator m,m1,m2;
    multimap<Data,int>::iterator d1,d2,d3;
    //使用Record应该先重载
    //multimap<Record,int>record;
    //multimap<Record,int>::iterator r;

    //Reader r;
    int i_;
    Data d;
public:
    //Readerop(){}; //这里初始的时候就直接构造文件内容,就不使用空构造了,测试这里时由于直接定义一开始没有导入文件内容,导致测不出东西
    //Readerop(int i,Data d){this->r=user[i],this->d=d,uload(),bload();}
    Readerop() {
   bload();}  //每个用户都应该能对书籍进行全面查询
    Readerop(int i1,Data d1):i_(i1),d(d1) {
   uload(),bload(),recload();}
    ~Readerop(){
   save();};
    Reader getUser() {
   return user[i_];}
    void uload();
    void bload();
    void recload();
    //void search(int j); //借书包含在搜索一栏中
    void lend(int j);
    void back();
    void xujie(int j);   //续借需要记录续借日期,然后从该日期开始记录一个月记为下次还书日期
    void save();
};

void Readerop::uload()
{
   

    //重复,但是要读取用户信息
    Reader u;
    ifstream infile("2018212591马俊光的用户.txt",ios::in);
    if(!infile)
    {
   
        cout<<"Can't load user!"<<endl;
        return;
    }
    //这里不再用map来查找等工作了,不再建立map
    user.clear();
    while(infile>>u)
    {
   
        user.push_back(u);
    }
    infile.close();
}

void Readerop::bload()
{
   
        //先导入书籍信息,方便查询与借阅
        Book b;
        ifstream in("2018212591马俊光的书籍.txt",ios::in);
        if(!in)
        {
   
            cout<<"Can't load book!"<<endl;
            return;
        }
        book.clear();
        bookname.clear();
        writer.clear();
        booknumber.clear();
        press.clear();
        publishdata.clear();
        while(in>>b)   //与增添书籍信息代码一致(后来讲到的的复用似乎可以使用)
        {
   
            //读取与保存时无需考虑重复情况,因为增添时已经进行了判断操作
            book.push_back(b);
            bookname.insert(make_pair(b.getName(),book.size()-1));
            writer.insert(make_pair(b.getWriter(),book.size()-1));
            booknumber.insert(make_pair(b.getNumber(),book.size()-1));
            press.insert(make_pair(b.getPress(),book.size()-1));
            publishdata.insert(make_pair(b.getdata(),book.size()-1));
        }
        in.close();
}

void Readerop::recload()
{
   
    Record rec1;
    ifstream in("2018212591马俊光的记录.txt",ios::in);
    if(!in)
    {
   
        cout<<"Can't load record!"<<endl;
        return;
    }
    rec.clear();
    recnumber.clear();
    //record.clear();
    while(in>>rec1)
    {
   
        rec.push_back(rec1);
        //record.insert(make_pair(rec1,rec.size()-1));
        recnumber.insert(make_pair(rec1.getBooknumber(),rec.size()-1));
    }
    in.close();
}

void Readerop::save()
{
   
    ofstream outfile("2018212591马俊光的用户.txt",ios::out);
    if(!outfile)
    {
   
        cout<<"Save User false!";
        return;
    }
    for(int i=0;i<user.size();i++)
    {
   
        outfile<<user[i];
    }
    outfile.close();
    //不止要保存用户信息,书籍信息也要重新进行调整,因为用户可能借书或还书,书籍信息可能有所变动
    ofstream outbook("2018212591马俊光的书籍.txt",ios::out);
    if(!outbook)
    {
   
        cout<<"Save Book false!";
        return;
    }
    for(int i=0;i<book.size();i++)
    {
   
        outbook<<book[i];
    }
    outbook.close();
    ofstream outrec("2018212591马俊光的记录.txt",ios::out);
    if(!outrec)
    {
   
        cout<<"Save record false!";
        return;
    }
    for(int i=0;i<rec.size();i++)
    {
   
        outrec<<rec[i];
    }
    outrec.close();
}

void Readerop::lend(int j)
{
   
    string in;
    cin>>in;
    int i=0;     //标记是否查到
    m=booknumber.find(in);
    if(m!=booknumber.end())
    {
   
        i=1;
        cout<<"Found"<<endl;   //这里算是提示,说明以下要进行输入并操作
        cout<<book[m->second];
        string caozuo;
        while(cin>>caozuo)
        {
   
            if(caozuo=="jieyue"||"return")
                break;
        }
        if(caozuo=="jieyue")
        {
   
            if(j>0)
                cout<<"对不起,您有超期的书未归还!"<<endl;
            else{
   
            int kejie=book[m->second].getKejie();
            if(user[i_].getJieyue()>=10)  //用户不能借:用户借书超限
            {
   
                //cout<<user[i_].getJieyue()<<endl;
                cout<<"以达到借阅上限"<<endl;
            }
            //判断是否超期
            else if(book[m->second].getKejie()<=0) //书籍库存不够,反之则可借
            {
   
                cout<<"Not enough!"<<endl;
                m1=recnumber.lower_bound(book[m->second].getNumber());
                m2=recnumber.upper_bound(book[m->second].getNumber());
                for(m=m1;m!=m2;m++)
                {
   
                    cout<<rec[m->second];  //缺点:所有记录都显示了(待处理)
                }
            }
            else    //可以借阅时
            {
   
                //这两个顺序不可变,一定是书籍信息先变化,然后用户信息再做调整,否则借阅的图书是借阅之前的信息,出现错误
                book[m->second].setKejie(--kejie);      //书籍信息作出修改变动
                user[i_].addjieyue(book[m->second],d);  //用户加新增书籍信息
                Record recc(book[m->second],user[i_].getName(),user[i_].getNumber(),d,"lend:");
                rec.push_back(recc);
                recnumber.insert(make_pair(recc.getBooknumber(),rec.size()-1));
            }
            cout<<user[i_];
        }
        }
        if(caozuo=="return");
    }
    if(i==0)
    cout<<"Not Found!"<<endl;
}

void Readerop::back()
{
   
    //还书首先要看自己借了哪本书(已借阅书籍全览),还书的操作也是靠书号的唯一性进行扫码还书(输入书号进行归还)
    //cout<<user[i_]; //登录时就完全显示借阅信息,这里其实不必重复显示
    string number;
    cin>>number;
    m=booknumber.find(number);
    if(m!=booknumber.end()){
   //确保书籍的合理性,提高效率
    //user[i_].backjieyue(book[m->second]);
    int ii_=0;
    ii_=user[i_].backjieyue(book[m->second]);  //调了很久一开始都没调好,原来函数在这里就被调用了,上一步的调用与之重复,当借阅重复书籍时这样重复还书就会出现问题,把上一步的调用去掉就好了
    if(ii_==1)  //还书成功,书籍信息作出相应调整
    {
   
        int kejie=book[m->second].getKejie();
        book[m->second].setKejie(++kejie);
        Record recc(book[m->second],user[i_].getName(),user[i_].getNumber(),d,"back:");
        rec.push_back(recc);
    }
    }
}
//续借与还书基本一致
void Readerop::xujie(int j)
{
   
    if(j>0)
        cout<<"对不起,您有超期的书未归还!"<<endl;
    else{
   
    int ii;
    string number;
    cin>>number;
    m=booknumber.find(number);
    if(m!=booknumber.end())
    {
   
        ii=user[i_].xujiejieyue(book[m->second],d);
        if(ii==1)//可续借,导入记录
        {
   
            Record recc(book[m->second],user[i_].getName(),user[i_].getNumber(),d,"xujie:");
            rec.push_back(recc);
        }
    }
    }
}

class Login
{
   
    vector<Reader>user;
    multimap<string,int>name;
    multimap<string,int>number;
    vector<Reader>::iterator it;
    multimap<string,int>::iterator m1,m2;
public:
    Login(){
   load();};
    ~Login(){
   };
    void load();
    void login();
};

void Login::load()
{
   
    //重复,但是要读取用户信息
    Reader u;
    ifstream infile("2018212591马俊光的用户.txt",ios::in);
    if(!infile)
    {
   
        cout<<"Can't load user!"<<endl;
        return;
    }
    user.clear();
    name.clear();
    number.clear();
    while(infile>>u)
    {
   
        user.push_back(u);
        name.insert(make_pair(u.getName(),user.size()-1));
        number.insert(make_pair(u.getNumber(),user.size()-1));
    }
    infile.close();
}

void Login::login()
{
   
    string name_,number_;
    int i=0;
    cout<<"请输入账号,密码:"<<endl;  //用户端账号即姓名,密码即借阅号(学号)(密码没有必要变动)(刷卡)
    cin>>name_>>number_;
    if(name_=="xiaoguang"&&number_=="123")
    {
   
        i=1;
        //调用管理端
        Guanliop guanli;
        //管理端的任意操作:(其实使用菜单会方便一些(便于操作))
        /* guanli.addBook(); guanli.addReader(); guanli.openBook(); guanli.openReader(); guanli.openRecord(); guanli.setBook(); guanli.setReader(); */

    }
    m2=number.find(number_);
    if(m2!=number.end())
    {
   
        i=1;
        //登录日期暂时由人为输入
        cout<<"请输入登录日期:"<<endl;
        Data d;
        cin>>d;
        Readerop reader(m2->second,d);
        cout<<reader.getUser();
        cout<<"Welcome back!"<<endl;  //必须输对有效的日期才行
        //判断是否存在书籍超期现象:
        int j=user[m2->second].chaoqi(d);  //对于超期的不可借阅不可续借
        //Readerop reader(m2->second,d);
       // readerop(m2->second,d);
        //调用用户端
        reader.search();
        reader.search();
        reader.lend(j);
        reader.lend(j);
        reader.xujie(j);
        reader.back();
    }
    if(i==0)
    cout<<"账号或密码输入有误"<<endl;
}
//登录


int main()
{
   
    //登录后进行的操作:每次登录:两次搜索,两次借阅,一次续借,一次还书
    Login op;
    int n=3;
    while(n--)
    op.login();
    return 0;
}

需要的文件:

2018212591马俊光的用户.txt
name: 马俊光 number: 2018212591 yijie: 2 kejie: 8
jieshuxinxi:
book.name: 《高等数学指导》 writer: 李仁所 book.number: 001 book.press: 清华大学出版社 book.publishdata: 2019 5 20 zongshu: 10 kejie: 7 guancang.not.lend: 2
lend: 2019 1 10 back: 2019 2 9 xujie: 0
book.name: 《向死而生》 writer: 李开复 book.number: 003 book.press: 人民邮电出版社 book.publishdata: 2019 5 1 zongshu: 7 kejie: 4 guancang.not.lend: 2
lend: 2019 1 10 back: 2019 2 9 xujie: 1
name: 孙林 number: 2018212488 yijie: 0 kejie: 10
name: ***中 number: 2018212598 yijie: 0 kejie: 10
name: 郎丰通 number: 2018212543 yijie: 0 kejie: 10
name: 常凯 number: 2018212555 yijie: 0 kejie: 10
2018212591马俊光的书籍.txt
book.name: 《高等数学指导》 writer: 李仁所 book.number: 001 book.press: 清华大学出版社 book.publishdata: 2019 5 20 zongshu: 10 kejie: 7 guancang.not.lend: 2
book.name: 《高等数学指导》 writer: 王志武 book.number: 002 book.press: 北京大学出版社 book.publishdata: 2019 5 21 zongshu: 6 kejie: 3 guancang.not.lend: 3
book.name: 《向死而生》 writer: 李开复 book.number: 003 book.press: 人民邮电出版社 book.publishdata: 2019 5 1 zongshu: 7 kejie: 4 guancang.not.lend: 2
book.name: 《挑战程序设计》 writer: 巫泽俊 book.number: 004 book.press: 人民邮电出版社 book.publishdata: 2010 1 1 zongshu: 8 kejie: 6 guancang.not.lend: 2
book.name: 《原则》 writer: 瑞·达利欧 book.number: 005 book.press: 人民文学出版社 book.publishdata: 2011 2 2 zongshu: 7 kejie: 5 guancang.not.lend: 2
book.name: 《英语美文阅读》 writer: 高珊 book.number: 006 book.press: 人民文学出版社 book.publishdata: 2012 3 3 zongshu: 12 kejie: 10 guancang.not.lend: 2
book.name: 《中国近代史纲要》 writer: 王书君 book.number: 007 book.press: 北京大学出版社 book.publishdata: 2013 4 4 zongshu: 15 kejie: 8 guancang.not.lend: 7
book.name: 《电子电路技术》 writer: 殷瑞祥 book.number: 008 book.press: 高等教育出版社 book.publishdata: 2014 5 5 zongshu: 6 kejie: 5 guancang.not.lend: 1
2018212591马俊光的记录.txt
lend: username: 马俊光 usernumber: 2018212591
book.name: 《高等数学指导》 writer: 李仁所 book.number: 001 book.press: 清华大学出版社 book.publishdata: 2019 5 20 zongshu: 10 kejie: 7 guancang.not.lend: 2
2019 1 1
lend: username: 马俊光 usernumber: 2018212591
book.name: 《高等数学指导》 writer: 王志武 book.number: 002 book.press: 北京大学出版社 book.publishdata: 2019 5 21 zongshu: 6 kejie: 2 guancang.not.lend: 3
2019 1 1
back: username: 马俊光 usernumber: 2018212591
book.name: 《高等数学指导》 writer: 李仁所 book.number: 001 book.press: 清华大学出版社 book.publishdata: 2019 5 20 zongshu: 10 kejie: 8 guancang.not.lend: 2
2019 5 1
xujie: username: 马俊光 usernumber: 2018212591
book.name: 《高等数学指导》 writer: 王志武 book.number: 002 book.press: 北京大学出版社 book.publishdata: 2019 5 21 zongshu: 6 kejie: 2 guancang.not.lend: 3
2019 1 10
lend: username: 马俊光 usernumber: 2018212591
book.name: 《高等数学指导》 writer: 李仁所 book.number: 001 book.press: 清华大学出版社 book.publishdata: 2019 5 20 zongshu: 10 kejie: 7 guancang.not.lend: 2
2019 1 10
lend: username: 马俊光 usernumber: 2018212591
book.name: 《向死而生》 writer: 李开复 book.number: 003 book.press: 人民邮电出版社 book.publishdata: 2019 5 1 zongshu: 7 kejie: 4 guancang.not.lend: 2
2019 1 10
xujie: username: 马俊光 usernumber: 2018212591
book.name: 《向死而生》 writer: 李开复 book.number: 003 book.press: 人民邮电出版社 book.publishdata: 2019 5 1 zongshu: 7 kejie: 4 guancang.not.lend: 2
2019 1 10
back: username: 马俊光 usernumber: 2018212591
book.name: 《高等数学指导》 writer: 王志武 book.number: 002 book.press: 北京大学出版社 book.publishdata: 2019 5 21 zongshu: 6 kejie: 3 guancang.not.lend: 3
2019 1 10
result部分解释:


代码中之前的注释部分删掉了。
先登录,按出版日期查询了一次,按索书号查询了一次,借书借了两本,续借一本还书一本(可见记录)
对于继承:这里只对查询进行了简单继承(写的能实现功能,但是不好,没有将书籍查询单独列为一个大类,就当做对继承的练习,继承的大体使用:通过调用基类函数实现派生类中相对重复的功能)