首页 > 技术文章 > 12306火车订票系统(C++)

study-hard-forever 2021-02-01 23:44 原文

12306火车订票系统(C++)

注意事项:

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

设计思路—设计问题解决思路、系统功能规划:

12306初始框架:
车次信息:车次编号,起始点,途经每一站的站点编号,站点名,几点到,几点开,中途间隔几分钟,两站点里程多少,某一站到某一站是否有票等
用户基础信息:姓名、身份证号、诚信度、购票记录等
管理员操作:
{
对车次信息:
{
一、对车次信息的查询:详细查询车次的各个信息(上面涉及的车次信息),还应包括对某两站点间的所有车次查询(按时间、按是否有票···)
二、对车次信息的增添与删除(添加新的车次信息或删除已有的车次;某一站点是否经过,是否要新增站点(这里里程信息,时间信息都要变动)等)
三、对车次信息的修改(几点到,几点开,中途间隔调度等)
}
对用户信息:
{
一、对用户信息的查询(姓名、身份证等基础信息,用户信用度,对其购票记录(这里仅限当天)等查询)
二、对用户信息的增删(新增用户与删除已有用户)(其实实际来说删除用户可能不会涉及)
三、对用户信息的修改(拉黑,诚信度低的禁止其购票)
}
}
用户基本操作:
{
一、对车次信息的查询:同管理员查询到的应是一样的
二、买票(该段路程有票下所有该段路涉及的小段车票减一(一定是对某一车次的车作检查与操作))、退票(所有该段路程涉及的小段加一)、改签(改签过程实际可看做是先退票后买票的过程)
三、对自身买票记录的查询
}
记录类:记录所有用户的操作(用户端只能查询自己的记录,直接包含在用户基本信息中,因此用户端没有必要再通过访问记录文件查询自己的购票记录,因此该记录只是面向管理端,管理端可通过用户名(身份证号),车次信息,车次起(始)点等信息进行查询)

继承关系:
管理员与用户都能对车次信息进行查询,故将车次信息的操作列为一类,管理端继承所有,用户端继承查询函数,其余屏蔽掉(查询不影响车次信息变化),(由于是继承,这里在管理端与用户端不再进行对车次信息的定义,相当于所有对车次信息的操作全部在车次信息类自己中)
管理端与用户端都对记录类进行继承,但是管理端有权查看所有记录消息,用户端只能查看自己的操作记录,因此同样执行查询功能显示的信息有所不同,可以使用同名覆盖函数,这里当时考虑到性质打算使用多态的形式,不过最后虽然用的虚函数,但是实质是使用同名覆盖函数实现的,并且这里对用户端查询进行了隐藏(实际所有的查询记录也都是存放在用户个人信息里的),这里可以进一步优化,使过期的记录(与登录时日期进行比较)在保存时自动清除,减少用户文件大小并且使文件更易读,那这样用户查询所有的操作记录(自己的),可以去记录文件查询输出。

具体实现—类设计、功能实现过程:

关于类设计与功能实现,很多细节方面的处理都在代码中具体体现,可以直接参考代码。
类设计:

class Time
class Site //所经过站点信息
class Gaotie //每一列的高铁信息

主要包含内容:

 	vector<Site>vsite;    //每一站的站点信息
    vector<Time>vend;    //每一站到站的时间点(起点的到站时间默认为发车时间)
    vector<Time>vstart;  //每一站发车的时间点(终点的发车时间默认为到站时间)
    vector<int>ticket;   //对于票数来说,这里每个站点都设立票数,但是买票时实际是买的从起点到终点这一区间段的票,其中区间段为左闭右开,因此列车的末站是永远有票的(一张也买不走)(这里只是进行一一对应关系,买票查票时再对其作处理)
    multimap<string,int>sitename;
    multimap<string,int>::iterator m1,m2;
    
	······················································

对于该类把所有的功能列一下:

 	string getNumber() {return number;}
    string getName() {return name;}
    //string getStart() {return start;}
    //string getEnd() {return end;}
    int getSeat() {return seat;}
    int getSiteshu() {return siteshu;}
    void setNumber(string number1) {number=number1;}
    void setName(string name1) {name=name1;}
    void setSeat(int seat1) {seat=seat1;}
    void setSiteshu(int siteshu1) {siteshu=siteshu1;}
    string searchbynumber(string start,string end);
    Time searchbytime(string start,string end);
    int searchbyhaoshi(string start,string end);
    Time findbytime(string start,string end,Time t);
    int findbyhaoshi(string start,string end,Time t);
    void searchout(string start,string end);  //显示所查到的车次信息
    int delete_();            //删除车次信息
    void zengsite();          //增加站点信息,很多时候其实是不太符合实际的(首尾站点例外)中间站点增加(删除)往往会伴随着其他站点信息到站发车时间的变化,不如直接删掉原来车次信息并增添新的车次信息简便
    void setsite();           //修改站点信息
    int deletesite();         //删除站点信息
    //高铁编号,高铁名可以改,但是对于起始站,终点站等必须要保证vector中数据与该数据同步,因此这里不必用set函数去直接修改,修改时修改vector信息,然后起始终点站同步即可(那么得知起始终点站也只需利用vector即可)
    int buy(string start,string end,Time t);
    int back(string start,string end,Time t);
    //改签是一个先退后补的过程,其实也可以调用back,buy函数,但是为了不输出多余信息(输出信息有所不同),这里单独再写两个改签函数
    int gaiqiantui(string start,string end,Time t);
    int gaiqianbu(string start,string end,Time t);
    //下面是买完车票之后对某些参数进行获取并生成记录
    Time endtime(string end);
    int s(string start,string end);
    
	···························································

class Gaotieop //对于高铁的操作(也是管理端与用户端直接继承的基类)

	 vector<Gaotie>vgaotie;
    //这里只用于查询起始点终点之间行程关系
    void gaotiesearch(string start1,string end1);
    void gaotiefind(string start1,string end1,Time t1);    //针对用户查询,用户端起始站点输入即可,但是时间必须传参导入,否则用户故意将时间更改,则变成了管理端查询,因此对于时间操作用户端是没有权限的,登录时系统直接记录
	························································

class Record //购票记录

class Getrecord

	//查询方式(主要针对管理端)
    //用户每进行一次操作所有的multimap都要作出相应改变(int值为vector最后一位,因为记录都是先买先记的,这样multimap也变得简单可行)
    //按用户名、身份证号来查
    multimap<string,int>mallrecordname;
    multimap<string,int>mallrecordnumber;
    //按所有的操作方式来查
    multimap<string,int>mallrecordcaozuo;
    //按车次信息来查
    multimap<string,int>mallrecordgaotienumber;
    //按出行时间来查
    multimap<Time,int>mallrecordstart;
························································

class User
class Guanliop:public Gaotieop,public Getrecord
class Userop:public Gaotieop,public Getrecord
class Login

具体的实现过程及细节请直接参考代码:

实现代码:

#include<bits/stdc++.h>
using namespace std;

class Time
{
    int year,month,day,hour,min;
    string s;
public:
    Time() {};
    ~Time() {};
    Time(int year1,int month1 ,int day1,int hour1,int min1):year(year1),month(month1),day(day1),hour(hour1),min(min1) {};
    friend istream& operator>>(istream&,Time&t);
    friend ostream& operator<< (ostream&,const Time&t);
    int operator- (Time t)  //重载运算符"-"在类内定义,类外定义由于year,month等是私有的,需使用get函数获取数据,没有必要
    {   //直接定义即可,不必使用引用,一开始觉得都一样,使用了引用,后来发现错误没法调了,调了很久才发现,使用引用后减号后面的值会变为减号前的值,两个值就一样了
        if((year<t.year)||(year==t.year&&month<t.month)||(year==t.year&&month==t.month&&day<t.day)||(year==t.year&&month==t.month&&day==t.day&&hour<t.hour)||year==t.year&&month==t.month&&day==t.day&&hour==t.hour&&min<t.min)
        return -1;
        int countyear=0;
        int countmonth=0;
        int countday=0;
        int counthour=0;
        int countmin=0;
        int m[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};  //按下标(对应月份)补齐,第一个数不计,对于每年的二月按28天计(不考虑平年闰年)
        while(1)
	    {
            if(this->year==t.year&&this->month==t.month&&this->day==t.day&&hour==t.hour&&min==t.min) break;
	        if(t.min==60)
	        {
	            t.hour++;
	            counthour++;
	            t.min=0;
                if(t.hour==24)
                {
                    t.day++;
                    countday++;
                    t.hour=0;
                    if(t.day>m[t.month])  //上述日期末尾天数是存在的,如果写"==",则直接跳过,不符实际
                    {
                        t.month++;
                        countmonth++;
                        t.day=1;
                        if(t.month>12)    //12月是存在的
                        {
                            t.year++;
                            countyear++;
                            t.month=1;
                        }
                    }
                }
	        }
	        else {t.min++;countmin++;}
        }
        //cout<<countyear<<" "<<countmonth<<" "<<countday<<" "<<counthour<<" "<<countmin<<endl;
        return countmin;
    }
    //Time operator+ (int i);  //利用"-"操作可以代替"+"
    //对于时间的比较,一开始不打算用的,不过后面的登录以及对用户对待出行车票的查看都需要用到
    bool operator< (const Time&t)const
    {
        return year!=t.year?year<t.year:month!=t.month?month<t.month:day!=t.day?day<t.day:hour!=t.hour?hour<t.hour:min<t.min;
    }
    bool operator== (const Time&t)const
    {
        return year==t.year&&month==t.month&&day==t.day&&hour==t.hour&&min==t.min;
    }
};

istream&operator>>(istream&in,Time&t)
{
    while(1)  //使用循环也好也不好,当程序运行时用户输入时间可以重新输入,但当文件内的时间有误时,程序会一直循环
    {
        //in>>t.year>>t.s>>t.month>>t.s>>t.day>>t.s>>t.hour>>t.s>>t.min;
        in>>t.year>>t.month>>t.day>>t.hour>>t.min;
        if(t.year>2000&&t.year<=2019&&t.month>=1&&t.month<=12&&t.day>=1&&t.day<=31&&t.hour>=0&&t.hour<=23&&t.min>=0&&t.min<60)   //对时间进行限制,还应注意过时车票不可买(查询也不可查,这可以在登录时间上作要求)
        break;
        else cout<<"Time error,请重试:"<<endl;
    }
    return in;
}

ostream&operator<<(ostream&out,const Time&t)
{
    out<<t.year<<" "<<t.month<<" "<<t.day<<" "<<t.hour<<" "<<t.min;
    return out;
}

/*
int main()
{
    /*
    Time t;
    int n=5;
    while(n--)
    {
        cin>>t;
        cout<<t;
    }
    return 0;

    Time t1,t2;
    int  n=5;
    while(n--)
    {
        cin>>t1>>t2;
        if(t2<t1)
        {
            cout<<t1-t2<<endl;
            cout<<"test"<<endl;
        }
    }
    return 0;
}
*/

class Site      //所经过站点信息
{
    string number,name;
    int s;     //表示里程,初始点记为零
public:
    Site() {};
    ~Site() {};
    Site(string number1,string name1,int s1):number(number1),name(name1),s(s1) {};
    string getNumber() {return number;}
    string getName() {return name;}
    int getS() {return s;}
    void setNumber(string number1) {number=number1;}
    void setName(string name1) {name=name1;}
    void setS(int s1) {s=s1;}
    friend istream& operator>> (istream&,Site&site);
    friend ostream& operator<< (ostream&,const Site&site);
    int operator- (Site site)
    {
        if(s<site.s) return -1;
        return s-site.s;
    }
};

istream& operator>> (istream&in,Site&site)
{
    while(1)
    {
        in>>site.number>>site.name>>site.s;
        if(site.s<0) cout<<"里程错误,请重新输入:"<<endl;
        else break;
    }
    return in;
}

ostream&operator<< (ostream&out,const Site&site)
{
    out<<site.number<<" "<<site.name<<" "<<site.s;
    return out;
}

/*
int main()
{
    /*
    Site s;
    int n=3;
    while(n--)
    {
        cin>>s;
        cout<<s<<endl;
    }

    Site s1,s2;
    int n=3;
    while(n--)
    {
        cin>>s1>>s2;
        cout<<s1-s2;
    }

    return 0;
}
*/

class Gaotie   //每一列的高铁信息
{
    string number,name;
    //string start,end;
    int seat;      //seat与ticket是两个不同的概念,seat表示容纳量,ticket表示票数,初始时(未售票时)seat=ticket
    int siteshu;  //表示站点数量(同时方便输入途经站点信息)
    vector<Site>vsite;    //每一站的站点信息
    vector<Time>vend;    //每一站到站的时间点(起点的到站时间默认为发车时间)
    vector<Time>vstart;  //每一站发车的时间点(终点的发车时间默认为到站时间)
    vector<int>ticket;   //对于票数来说,这里每个站点都设立票数,但是买票时实际是买的从起点到终点这一区间段的票,其中区间段为左闭右开,因此列车的末站是永远有票的(一张也买不走)(这里只是进行一一对应关系,买票查票时再对其作处理)
    multimap<string,int>sitename;
    multimap<string,int>::iterator m1,m2;
public:
    Gaotie() {};
    ~Gaotie() {};
    Gaotie(string number1,string name1,int seat1):number(number1),name(name1),seat(seat1) {};
    string getNumber() {return number;}
    string getName() {return name;}
    //string getStart() {return start;}
    //string getEnd() {return end;}
    int getSeat() {return seat;}
    int getSiteshu() {return siteshu;}
    void setNumber(string number1) {number=number1;}
    void setName(string name1) {name=name1;}
    void setSeat(int seat1) {seat=seat1;}
    void setSiteshu(int siteshu1) {siteshu=siteshu1;}
    string searchbynumber(string start,string end);
    Time searchbytime(string start,string end);
    int searchbyhaoshi(string start,string end);
    Time findbytime(string start,string end,Time t);
    int findbyhaoshi(string start,string end,Time t);
    void searchout(string start,string end);  //显示所查到的车次信息
    int delete_();            //删除车次信息
    void zengsite();          //增加站点信息,很多时候其实是不太符合实际的(首尾站点例外)中间站点增加(删除)往往会伴随着其他站点信息到站发车时间的变化,不如直接删掉原来车次信息并增添新的车次信息简便
    void setsite();           //修改站点信息
    int deletesite();         //删除站点信息
    //高铁编号,高铁名可以改,但是对于起始站,终点站等必须要保证vector中数据与该数据同步,因此这里不必用set函数去直接修改,修改时修改vector信息,然后起始终点站同步即可(那么得知起始终点站也只需利用vector即可)
    int buy(string start,string end,Time t);
    int back(string start,string end,Time t);
    //改签是一个先退后补的过程,其实也可以调用back,buy函数,但是为了不输出多余信息(输出信息有所不同),这里单独再写两个改签函数
    int gaiqiantui(string start,string end,Time t);
    int gaiqianbu(string start,string end,Time t);
    //下面是买完车票之后对某些参数进行获取并生成记录
    Time endtime(string end);
    int s(string start,string end);
    friend istream& operator>> (istream&,Gaotie&g);  //重载输入对于向量(站点)首先应输入站点对象,然后压入向量,时间信息也是如此
    friend ostream& operator<< (ostream&,Gaotie&g);
};

void Gaotie::searchout(string start,string end) //根据后面的代码需要重新改的
{
    int ticket_=9999999;
    m1=sitename.find(start);
    m2=sitename.find(end);
    if(m1!=sitename.end()&&m2!=sitename.end())
    {
        if(m1->second<m2->second&&vsite[m1->second].getName()!="0"&&vsite[m2->second].getName()!="0")
        {   //上面是防止删除站点时发生的情况(multimap中站点信息没清,vector中已清除)
            //其实是没有必要再去判断,因为这里是已经判断完毕的
            cout<<getNumber()<<" "<<getName()<<endl;
            cout<<start<<" "<<vstart[m1->second]<<endl;
            cout<<end<<" "<<vend[m2->second]<<endl;
            //cout<<"剩余票数:1000"<<endl;
            for(int i=m1->second;i<m2->second;i++)  //左闭右开区间
            {
                if(ticket[i]<ticket_)
                ticket_=ticket[i];  //从起点至终点的一段路程中,票数最少的一段即为该段路程的剩余票数
            }
            cout<<"剩余票数:"<<ticket_<<endl<<endl;
            //return 1;   //表示车次信息存在
        }
    }
    //return -1;   //表示无车次信息
}

string Gaotie::searchbynumber(string start,string end)
{
    m1=sitename.find(start);
    m2=sitename.find(end);
    if(m1!=sitename.end()&&m2!=sitename.end())
    {
        if(m1->second<m2->second&&vsite[m1->second].getName()!="0"&&vsite[m2->second].getName()!="0")
        return number;
    }
    return "0";
}

Time Gaotie::searchbytime(string start,string end)
{
    m1=sitename.find(start);
    m2=sitename.find(end);
    if(m1!=sitename.end()&&m2!=sitename.end())
    {
        if(m1->second<m2->second&&vsite[m1->second].getName()!="0"&&vsite[m2->second].getName()!="0")
        return vstart[m1->second];
    }
    Time t_(0,0,0,0,0);
    return t_;
}

int Gaotie::searchbyhaoshi(string start,string end)
{
    m1=sitename.find(start);
    m2=sitename.find(end);
    if(m1!=sitename.end()&&m2!=sitename.end())
    {
        if(m1->second<m2->second&&vsite[m1->second].getName()!="0"&&vsite[m2->second].getName()!="0")
        {
            return vend[m2->second]-vstart[m1->second];
        }
    }
    return -1;
}

Time Gaotie::findbytime(string start,string end,Time t)
{
    m1=sitename.find(start);
    m2=sitename.find(end);
    if(m1!=sitename.end()&&m2!=sitename.end())
    {
        if(m1->second<m2->second&&vsite[m1->second].getName()!="0"&&vsite[m2->second].getName()!="0"&&t<vstart[m1->second])
        return vstart[m1->second];
    }
    Time t_(0,0,0,0,0);
    return t_;
}

int Gaotie::findbyhaoshi(string start,string end,Time t)
{
    m1=sitename.find(start);
    m2=sitename.find(end);
    if(m1!=sitename.end()&&m2!=sitename.end())
    {
        if(m1->second<m2->second&&vsite[m1->second].getName()!="0"&&vsite[m2->second].getName()!="0"&&t<vstart[m1->second])
        {
            return vend[m2->second]-vstart[m1->second];
        }
    }
    return -1;
}

int Gaotie::delete_()
{
    int x=0;
    for(int i=0;i<ticket.size();i++)
    {
        if(ticket[i]!=seat)
        {
            x=1;
            cout<<"对不起,该列车已售票,无法删除!";
            break;
        }
    }
    if(x==0)
    {
        number="0";
        return 1;
    }
    return -1;
}

void Gaotie::setsite()
{
    string caozuo_;
    cin>>caozuo_;
    m1=sitename.find(caozuo_);
    //int x=m1->second;
    if(m1!=sitename.end())
    {
        cout<<vsite[m1->second]<<" "<<vend[m1->second]<<" "<<vstart[m1->second]<<endl;
        string number_,name_;
        int s_;
        Time start_,end_;
        cin>>number_>>name_>>s_>>end_>>start_;
        vsite[m1->second].setNumber(number_);
        vsite[m1->second].setName(name_);
        vsite[m1->second].setS(s_);
        vend[m1->second]=end_;
        vstart[m1->second]=start_;
        sitename.insert(make_pair(name_,m1->second));
        sitename.erase(m1);
    }
    else cout<<"无该站点信息!"<<endl;
}

int Gaotie::deletesite()
{
    string caozuo_;
    cin>>caozuo_;
    m1=sitename.find(caozuo_);
    if(m1!=sitename.end())
    {
        cout<<vsite[m1->second]<<" "<<vend[m1->second]<<" "<<vstart[m1->second]<<endl;
        vsite[m1->second].setName("0");  //这里对站点信息的查询作了操作,不必删除multimap中信息即可
        setSiteshu(getSiteshu()-1);
        return 1;
        //这里也可以直接对以后的站点信息的时间作修改(到站出发时间统一加上删除点的停靠时间即可)
        //当然我们买完票也是无法更改之前的时间的(票已经拿到手了)我们能查询的是超时了几分钟
        /*
            cout<<"删除成功! 是否要对该站以后的站点时间作修改?"<<endl;
            cin>>caozuo_;
            if(caozuo_=="NO");
            if(caozuo_=="YES")
            {
                int shijian=vstart[m1->second]-vend[m1->second];
                for(int k=m1->second+1;k!=vsite.size();k++)
                {
                    //vend[k]=vend[k]+shijian;
                    //vstart[k]=vstart[k]+shijian;
                    //需要对时间重载加号
                }
            }
        */
    }
    else cout<<"无该站点信息!"<<endl;
    return -1;
}

/*
//不太符合实际(首尾站点例外)中间站点增加(删除)往往会伴随着其他站点信息到站发车时间的变化,不如直接删掉原来车次信息并增添新的车次信息简便
void zengsite()
{
    //这里只是对尾站作添加,并不符合实际,中途站点的添加直接通过增删列车信息实现(最简单的也可以通过数据文件直接增添)
    //如果非要在起始点或者中间位置添加车站信息的话,对于中间位置可以通过输入要导入站点两侧的站点名,然后新建一个vector<Site>导入要建立站点左侧的站点信息,然后导入该站点信息,再导入该站点右侧站点信息,清空原来的vector,导入所有站点信息,对于起始点类似
    //然后对multimap进行重建,站点数进行更新
    Site site;
    Time time;
    cin>>site;
    vsite.push_back(site);
    cin>>time;
    vend.push_back(time);
    cin>>time;
    vstart.push_back(time);
    sitename.insert(make_pair(site.getName(),vsite.size()-1));
    ticket.push_back(seat);
    siteshu++;
}
*/

int Gaotie::buy(string start,string end,Time t)
{
    m1=sitename.find(start);
    m2=sitename.find(end);
    if(m1!=sitename.end()&&m2!=sitename.end()&&vsite[m1->second].getName()!="0"&&vsite[m2->second].getName()!="0"&&m1->second<m2->second)
    {
        if(t==vstart[m1->second])  //导入的时间是发车时间
        {
            int ticket_=9999999;
            for(int i=m1->second;i<m2->second;i++)  //左闭右开区间
            {
                if(ticket[i]<ticket_)
                ticket_=ticket[i];  //从起点至终点的一段路程中,票数最少的一段即为该段路程的剩余票数
            }
            if(ticket_>=1)
            {
                for(int i=m1->second;i<m2->second;i++)
                {
                    --ticket[i];
                }
                //cout<<"恭喜您已成功买到该车票"<<endl;
                return 1;
            }
            else {cout<<"对不起,您要买的车票已售空,请选择其他车次!"<<endl; return -1;}
        }
        else {cout<<"对不起,您输入的发车时间有误,请您核对车次信息后重新购票!"<<endl; return -1;}
        //下面的时间已经在初始时判断了
        //else {cout<<"对不起,您要买的车票已过期"<<endl; return -1;}  //其实基本上不会出现这种情况,因为用户根本查不到过期车票的车次信息
    }
    else {cout<<"对不起,您输入的起始站点信息有误!"<<endl; return -1;}
}

int Gaotie::back(string start,string end,Time t)
{
    m1=sitename.find(start);
    m2=sitename.find(end);
    if(m1!=sitename.end()&&m2!=sitename.end()&&vsite[m1->second].getName()!="0"&&vsite[m2->second].getName()!="0"&&m1->second<m2->second)
    {
        if(t==vstart[m1->second])  //导入的时间是发车时间
        {
            for(int i=m1->second;i<m2->second;i++)
            {
                ++ticket[i];
            }
            //cout<<"恭喜您已成功退票!"<<endl;
            return 1;
        }
        else {cout<<"对不起,您输入的发车时间有误,请您核对车次信息后重新退票!"<<endl; return -1;}
    }
    else {cout<<"对不起,您输入的起始站点信息有误!"<<endl; return -1;}
}

int Gaotie::gaiqianbu(string start,string end,Time t)
{
    m1=sitename.find(start);
    m2=sitename.find(end);
    if(m1!=sitename.end()&&m2!=sitename.end()&&vsite[m1->second].getName()!="0"&&vsite[m2->second].getName()!="0"&&m1->second<m2->second)
    {
        if(t==vstart[m1->second])  //导入的时间是发车时间
        {
            int ticket_=9999999;
            for(int i=m1->second;i<m2->second;i++)  //左闭右开区间
            {
                if(ticket[i]<ticket_)
                ticket_=ticket[i];  //从起点至终点的一段路程中,票数最少的一段即为该段路程的剩余票数
            }
            if(ticket_>=1)
            {
                //有票但是先不买,先做一个判断,保证改签前车票与改签后车票都是正确的
                return 1;
            }
            else {cout<<"对不起,您要买的车票已售空,请选择其他车次!"<<endl; return -1;}
        }
        else {cout<<"对不起,您输入的发车时间有误,请您核对车次信息后重新购票!"<<endl; return -1;}
    }
}

int Gaotie::gaiqiantui(string start,string end,Time t)
{
    m1=sitename.find(start);
    m2=sitename.find(end);
    if(m1!=sitename.end()&&m2!=sitename.end()&&vsite[m1->second].getName()!="0"&&vsite[m2->second].getName()!="0"&&m1->second<m2->second)
    {
        if(t==vstart[m1->second])  //导入的时间是发车时间
        {
            return 1;
        }
        else {cout<<"对不起,您输入的发车时间有误,请您核对车次信息后重新退票!"<<endl; return -1;}
    }
    else {cout<<"对不起,您输入的起始站点信息有误!"<<endl; return -1;}
}

Time Gaotie::endtime(string end)
{
    m1=sitename.find(end);
    if(m1!=sitename.end()&&vsite[m1->second].getName()!="0")
    {
        return vend[m1->second];
    }
    else //其实不会出现这种情况
    {
        Time t_(0,0,0,0,0);
        return t_;
    }
}

int Gaotie::s(string start,string end)
{
    m1=sitename.find(start);
    m2=sitename.find(end);
    if(m1!=sitename.end()&&m2!=sitename.end()&&vsite[m1->second].getName()!="0"&&vsite[m2->second].getName()!="0"&&m1->second<m2->second)
    {
        return (vsite[m2->second].getS()-vsite[m1->second].getS());
    }
    else return -1;
}

istream& operator>> (istream&in,Gaotie&g)
{
    in>>g.number>>g.name>>g.seat>>g.siteshu;
    g.vsite.clear();
    g.vend.clear();
    g.vstart.clear();
    g.ticket.clear();
    g.sitename.clear();
    Site site;
    Time time;
    int tic;  //表示票数
    for(int i=1;i<=g.siteshu;i++)
    {
        in>>site;
        g.vsite.push_back(site);
        in>>time;
        g.vend.push_back(time);
        in>>time;
        g.vstart.push_back(time);
        g.sitename.insert(make_pair(site.getName(),g.vsite.size()-1));
        //g.ticket.push_back(g.seat);  //初始时可以这么设定,但是买完票以后这么读取文件就不对了
        in>>tic;
        g.ticket.push_back(tic);
    }
    return in;
}

ostream& operator<< (ostream&out,Gaotie&g)
{
    out<<g.number<<" "<<g.name<<" "<<g.seat<<" "<<g.siteshu<<endl;
    for(int i=0;i<g.vsite.size();i++)
    {
        if(g.vsite[i].getName()!="0")
        out<<g.vsite[i]<<" "<<g.vend[i]<<" "<<g.vstart[i]<<" "<<g.ticket[i]<<endl;
    }
    return out;
}

/*
int main()
{
    Gaotie g;
    int n=3;
    while(n--)
    {
        cin>>g;
        cout<<g;
    }
    return 0;
}
*/

class Gaotieop
{
protected:
    vector<Gaotie>vgaotie;
    int gaotieshu;   //读取时利用while(infile>>g)总是读取失败,限定车次数量读取即可,还可用于显示每天的总车次
    multimap<string,int>gaotienumber;
    multimap<string,int>gaotiename;
    multimap<int,int>siteshu;
    multimap<string,int>::iterator m;
    multimap<int,int>::iterator m1,m2,m3;
public:
    Gaotieop() {load();};
    ~Gaotieop() {save();};
    int getGaotieshu() {return gaotieshu;}
    void setGaotieshu(int gaotieshu1) {gaotieshu=gaotieshu1;}
    void load();
    void save();
    void allgaotie();
    //这里只用于查询起始点终点之间行程关系
    void gaotiesearch(string start1,string end1);
    void gaotiefind(string start1,string end1,Time t1);    //针对用户查询,用户端起始站点输入即可,但是时间必须传参导入,否则用户故意将时间更改,则变成了管理端查询,因此对于时间操作用户端是没有权限的,登录时系统直接记录
    void gaotiezeng();
    void gaotieset();
    void gaotiedelete();
};

void Gaotieop::load()
{
    Gaotie g;
    int gaotieshu_;
    ifstream infile("2018212591马俊光的车次信息.txt",ios::in);
    if(!infile)
    {
        cout<<"can't load Gaotie!"<<endl;
        return;
    }
    vgaotie.clear();
    gaotienumber.clear();
    gaotiename.clear();
    siteshu.clear();
    infile>>gaotieshu_;
    setGaotieshu(gaotieshu_);
    for(int i=1;i<=gaotieshu_;i++)
    {
        infile>>g;
        vgaotie.push_back(g);
        gaotienumber.insert(make_pair(g.getNumber(),vgaotie.size()-1));
        gaotiename.insert(make_pair(g.getName(),vgaotie.size()-1));
        siteshu.insert(make_pair(g.getSiteshu(),vgaotie.size()-1));
    }
    infile.close();
    //test
    /*
    cout<<getGaotieshu()<<endl;
    for(int i=0;i<vgaotie.size();i++)
    cout<<vgaotie[i];
    */
}

void Gaotieop::save()
{
    ofstream outfile("2018212591马俊光的车次信息.txt",ios::out);
    if(!outfile)
    {
        cout<<"can't save Gaotie!"<<endl;
        return;
    }
    outfile<<getGaotieshu()<<endl;
    for(int i=0;i<vgaotie.size();i++)
    {
        if(vgaotie[i].getNumber()!="0")
        outfile<<vgaotie[i];
    }
    outfile.close();
    /*
    //test
    cout<<getGaotieshu()<<endl;
    for(int i=0;i<vgaotie.size();i++)
    cout<<vgaotie[i];
    */
}

/*
int main()
{
    Gaotieop op;
    op.load();
    op.save();
    return 0;
}
*/

void Gaotieop::gaotiesearch(string start1,string end1)  //针对管理员,输入起始点能查看所有车次信息(用户只能查看截止时间后的车次)
{
    //test
    //总览
    //按列车号    直接排序查找输出
    //按发车时间  返回时间,建map排序输出
    //按耗时最短  返回起始点时间差(int),建map排序
    //根据不同的返回方式不易判断是否存在该起始点的车次信息,可以分别写几个函数
    //限时间(只看列车未出发的车次信息)(gaotiefind())
    //限时间其实是用户使用时的查询,用户端只能查未出行的车次信息,没有权限去访问已过期的车次
    string start=start1,end=end1;
    string caozuo;
    cin>>caozuo;
    if(caozuo=="number")
    {
        string s;
        multimap<string,int>m;
        multimap<string,int>::iterator mm;
        for(int i=0;i<vgaotie.size();i++)
        {
            if(vgaotie[i].getNumber()!="0")
            {
                s=vgaotie[i].searchbynumber(start,end);
                if(s!="0")
                m.insert(make_pair(s,i));
            }
        }
        if(!m.size())
            cout<<"对不起,无该车次信息!"<<endl;
        else
        {
            for(mm=m.begin();mm!=m.end();mm++)
                vgaotie[mm->second].searchout(start,end);
                //cout<<vgaotie[mm->second]<<endl;
                //已经查到站点信息了,可以通过函数输出站点具体信息
        }
    }
    if(caozuo=="time")
    {
        Time t_;
        Time tt(0,0,0,0,0);
        multimap<Time,int>m;
        multimap<Time,int>::iterator mm;
        for(int i=0;i<vgaotie.size();i++)
        {
            if(vgaotie[i].getNumber()!="0")
            {
                t_=vgaotie[i].searchbytime(start,end);
                if(t_==tt);
                else
                {
                    m.insert(make_pair(t_,i));
                }
            }
        }
        if(!m.size())
        cout<<"对不起,无该车次信息!"<<endl;
        else
        {
            for(mm=m.begin();mm!=m.end();mm++)
                vgaotie[mm->second].searchout(start,end);
                //cout<<vgaotie[mm->second]<<endl;
        }
    }
    if(caozuo=="haoshi")
    {
        int x=0,y=0;
        multimap<int,int>m;
        multimap<int,int>::iterator mm;
        for(int i=0;i<vgaotie.size();i++)
        {
            if(vgaotie[i].getNumber()!="0")
            {
                x=vgaotie[i].searchbyhaoshi(start,end);
                if(x!=-1)
                {
                    y=1;
                    m.insert(make_pair(x,i));
                }
            }
        }
        if(y==0)  //也可以直接判断m.size()
            cout<<"对不起,无该车次信息!"<<endl;
        else
        {
            for(mm=m.begin();mm!=m.end();mm++)
            {
                cout<<mm->first<<" ";  //表示耗时
                vgaotie[mm->second].searchout(start,end);
                //cout<<mm->first<<" "<<vgaotie[mm->second]<<endl;
            }
        }
    }
    if(caozuo=="find")
    {
        Time t;
        cin>>t;
        gaotiefind(start,end,t);
    }
}

void Gaotieop::gaotiefind(string start1,string end1,Time t1)
{
    string start=start1,end=end1;
    Time t=t1;
    //cin>>start>>end>>t;
    string caozuo;
    cin>>caozuo;
    if(caozuo=="time")  //最早出发,返回时间进行比较
    {
        Time t_;
        Time tt(0,0,0,0,0);
        multimap<Time,int>m;
        multimap<Time,int>::iterator mm;
        for(int i=0;i<vgaotie.size();i++)  //查询车票起始点时是需要遍历所有列车信息的,看哪一班列车有要出行的该段路程
        {
            if(vgaotie[i].getNumber()!="0")
            {
                t_=vgaotie[i].findbytime(start,end,t);
                if(t_==tt);
                else
                {
                    m.insert(make_pair(t_,i));
                }
            }
        }
        if(!m.size())
            cout<<"对不起,无该车次信息!"<<endl;
        else
        {
            for(mm=m.begin();mm!=m.end();mm++)
                vgaotie[mm->second].searchout(start,end);
                //cout<<vgaotie[mm->second]<<endl;
        }
    }
    if(caozuo=="haoshi")  //返回int,耗时多少进行比较
    {
        int x=0,y=0;
        multimap<int,int>m;
        multimap<int,int>::iterator mm;
        for(int i=0;i<vgaotie.size();i++)
        {
            if(vgaotie[i].getNumber()!="0")
            {
                x=vgaotie[i].findbyhaoshi(start,end,t);
                if(x!=-1)
                {
                    y=1;
                    m.insert(make_pair(x,i));
                }
            }
        }
        if(y==0)  //也可以直接判断m.size()
        {
            cout<<"对不起,无该车次信息!"<<endl;
            return;
        }
        else
        {
            for(mm=m.begin();mm!=m.end();mm++)
            {
                cout<<mm->first<<" ";
                vgaotie[mm->second].searchout(start,end);
                //cout<<mm->first<<" "<<vgaotie[mm->second]<<endl;
            }
        }
    }
}

/*
int main()
{
    Gaotieop op;
    op.load();
    int n=5;
    while(n--)
    {
        op.gaotiesearch();
    }
    return 0;
}
*/

/*
int main()
{
    Gaotieop op;
    int n=5;
    string start,end;
    //Time t;
    while(n--)
    {
        //cin>>start>>end>>t;
        //op.gaotiefind(start,end,t);
        cin>>start>>end;
        op.gaotiesearch(start,end);
    }
    return 0;
}
*/

void Gaotieop::allgaotie()
{
    string caozuo;
    cin>>caozuo;
    if(caozuo=="number")
    {
        for(m=gaotienumber.begin();m!=gaotienumber.end();m++)
        if(vgaotie[m->second].getNumber()!="0")
        cout<<vgaotie[m->second];
    }
    else if(caozuo=="name")
    {
        for(m=gaotiename.begin();m!=gaotiename.end();m++)
        if(vgaotie[m->second].getNumber()!="0")
        cout<<vgaotie[m->second];
    }
    else if(caozuo=="siteshu1")  //表示站点数按增序列排序
    {
        for(m1=siteshu.begin();m1!=siteshu.end();m1++)
        if(vgaotie[m1->second].getNumber()!="0")
        cout<<vgaotie[m1->second];
    }
    else if(caozuo=="siteshu2")  //表示站点数按减序列排序
    {
        for(m1=(--siteshu.end());m1!=siteshu.begin();m1--)
        if(vgaotie[m1->second].getNumber()!="0")
        cout<<vgaotie[m1->second];
        if(vgaotie[siteshu.begin()->second].getNumber()!="0")
        cout<<vgaotie[siteshu.begin()->second];
    }
}

void Gaotieop::gaotiezeng()
{
    Gaotie g;
    cin>>g;
    vgaotie.push_back(g);
    gaotienumber.insert(make_pair(g.getNumber(),vgaotie.size()-1));
    gaotiename.insert(make_pair(g.getName(),vgaotie.size()-1));
    siteshu.insert(make_pair(g.getSiteshu(),vgaotie.size()-1));
}

/*
int main()
{
    Gaotieop op;
    op.load();
    /*
    int n=5;
    while(n--)
    {
        op.allgaotie();
    }

    op.gaotiezeng();
    op.allgaotie();
    op.allgaotie();
    op.save();

}
*/

void Gaotieop::gaotiedelete()
{
    string number;
    cin>>number;
    m=gaotienumber.find(number);
    if(m!=gaotienumber.end())
    {
        //查到列车确实可以直接删掉,但是我们要考虑是否该车已经卖了票出去,如果卖了,则不能删。否则直接删掉停运即可
        int x=vgaotie[m->second].delete_();
        if(x==1) setGaotieshu(getGaotieshu()-1);
    }
}

void Gaotieop::gaotieset()
{
    string number;
    cin>>number;
    int x=0;
    m=gaotienumber.find(number);
    if(m!=gaotienumber.end())
    {
        x=1;
        int y=m->second;    //为防止变化,统一改为y
        cout<<vgaotie[y];   //这里如果要查询的车次已删除的话,编号信息已归零,不能查询到
        //对于高铁信息作出修改,就像删除高铁车次一样,当售出车票时不能对高铁信息的某些选项进行修改
        //修改都应建立在未售票的前提基础之上
        //不过这里的超时可以直接对以后的站点信息进行修改
        string caozuo,caozuo_;
        int i;
        while(1)
        {
            cin>>caozuo;
            if(caozuo=="name")
            {
                cin>>caozuo_;
                m=gaotiename.find(vgaotie[y].getName());  //先取得之前的名称(先删去multimap,先改vector则之前名称查不到)
                gaotiename.erase(m);
                gaotiename.insert(make_pair(caozuo_,y));
                vgaotie[y].setName(caozuo_);
            }
            if(caozuo=="number")
            {
                cin>>caozuo_;
                vgaotie[y].setNumber(caozuo_);
                gaotienumber.erase(m);
                gaotienumber.insert(make_pair(caozuo_,y));
            }
            if(caozuo=="seat")
            {
                cin>>i;
                vgaotie[y].setSeat(i);
            }
            if(caozuo=="site")
            {
                cin>>caozuo_;
                if(caozuo_=="set")
                {
                    vgaotie[y].setsite();
                }
                else if(caozuo_=="delete")
                {
                    int z=vgaotie[y].deletesite();
                    if(z==1)
                    {
                        /*
                        for(m1=siteshu.begin();m1!=siteshu.end();m1++)
                        if(m1->first==vgaotie[y].getSiteshu()+1&&m1->second==y)
                        {
                            siteshu.erase(m1);
                            break;
                        }
                        */
                        //简化上面的遍历(其实站点信息并不多,最多也就二三十个)
                        //m1=siteshu.find(vgaotie[y].getSiteshu()+1);
                        m2=siteshu.lower_bound(vgaotie[y].getSiteshu()+1);
                        m3=siteshu.upper_bound(vgaotie[y].getSiteshu()+1);
                        for(m1=m2;m1!=m3;m1++)
                        {
                            if(m1->second==y)
                            {
                                siteshu.erase(m1);
                                break;
                            }
                        }
                        siteshu.insert(make_pair(vgaotie[y].getSiteshu(),y));
                    }
                }
            }
            if(caozuo=="return")
            break;
        }
    }
    if(x==0)
    cout<<"对不起,无该车次信息!"<<endl;
}

/*
int main()
{
    Gaotieop op;
    op.load();
    int n=2;
    while(n--)
    {
        op.gaotiedelete();
        op.allgaotie();
        op.gaotiesearch();
    }
    op.save();
    return 0;
}
*/

/*
int main()
{
    Gaotieop op;
    op.load();
    int x=3,y=3;

    while(x--)
    {
        op.gaotieset();
        op.allgaotie();
    }

    while(y--)
    {
        op.gaotieset();
        op.allgaotie();
        op.gaotiesearch();  //由于操作中可能要对站点信息进行修改,这里列出对起始站点的车次信息查询
    }
    op.save();
}
*/

class Record  //购票记录
{
    //不显示用户购票的时间,没必要,只显示车票信息,用户信息及操作状态
    string username,usernumber,caozuo;
    string gaotienumber,gaotiename,startsite,endsite;
    int s;
    Time starttime,endtime;
    //Gaotie g; //显示总的车次信息(没必要)
public:
    Record() {};
    Record(string username1,string usernumber1,string caozuo1,string gaotienumber1,string gaotiename1,string startsite1,Time starttime1,string endsite1,Time endtime1,int sss):username(username1),usernumber(usernumber1),caozuo(caozuo1),gaotienumber(gaotienumber1),gaotiename(gaotiename1),startsite(startsite1),starttime(starttime1),endsite(endsite1),endtime(endtime1),s(sss) {};
    ~Record() {};
    //下面的get函数是针对用户端显示记录用(用户端如果每次都显示人名,身份证号显得很多余),管理端每条记录都要对应人名等身份信息,因此对于管理端输出记录整体信息
    string getCaozuo() {return caozuo;}
    string getGaotienumber() {return gaotienumber;}
    string getGaotiename() {return gaotiename;}
    string getStartsite() {return startsite;}
    string getEndsite() {return endsite;}
    int getS() {return s;}
    Time getStarttime() {return starttime;}
    Time getEndtime() {return endtime;}
    //对于记录的查找(当利用用户信息查找时仍需要得知用户信息)
    string getName() {return username;}
    string getNumber() {return usernumber;}
    void setUsername(string username1) {username=username1;}
    friend istream& operator>> (istream&,Record&r);
    friend ostream& operator<< (ostream&,const Record&r);
};

istream& operator>> (istream&in,Record&r)
{
    in>>r.username>>r.usernumber>>r.caozuo;
    in>>r.gaotienumber>>r.gaotiename>>r.startsite>>r.starttime>>r.endsite>>r.endtime>>r.s;  //里程自动生成即可
    return in;
}

ostream& operator<< (ostream&out,const Record&r)
{
    out<<r.username<<" "<<r.usernumber<<" "<<r.caozuo<<endl;
    out<<r.gaotienumber<<" "<<r.gaotiename<<" "<<r.startsite<<" "<<r.starttime<<" "<<r.endsite<<" "<<r.endtime<<" "<<r.s<<endl;
    return out;
}

/*
int main()
{
    Record r;
    cin>>r;
    cout<<r;
    return 0;
}
*/

//对于读取记录来说可以考虑使用多态(用户所能查询的记录是之前所有买过(退过、改签)的票数,而管理端能查询的是所有用户的操作记录
//不同的访问结果,但是执行的都是对记录的访问功能,这里把记录单列为一个类方便管理端与用户端进行查找
class Getrecord
{
protected:
    vector<Record>allrecord;  //应考虑用户操作完(买、退、补票)对该记录进行导入
    int allrecordshu;
    //查询方式(主要针对管理端)
    //用户每进行一次操作所有的multimap都要作出相应改变(int值为vector最后一位,因为记录都是先买先记的,这样multimap也变得简单可行)
    //按用户名、身份证号来查
    multimap<string,int>mallrecordname;
    multimap<string,int>mallrecordnumber;
    //按所有的操作方式来查
    multimap<string,int>mallrecordcaozuo;
    //按车次信息来查
    multimap<string,int>mallrecordgaotienumber;
    //按出行时间来查
    multimap<Time,int>mallrecordstart;
public:
    Getrecord() {recordload();};
    ~Getrecord() {recordsave();};
    int getAllrecordshu() {return allrecordshu;}
    void setAllrecordshu(int allrecordshu1) {allrecordshu=allrecordshu1;}
    void recordload();
    void recordsave();
    virtual void getRecord()=0;  //定义为纯虚函数,到管理操作或用户操作查询时各自进行不同定义
    //使用纯虚函数Getrecord变成抽象类,便不能定义对象
};

void Getrecord::recordload()
{
    Record r;
    ifstream infile("2018212591马俊光的购票记录.txt",ios::in);
    if(!infile)
    {
        cout<<"can't load record!"<<endl;
        return;
    }
    allrecord.clear();
    mallrecordname.clear();
    mallrecordnumber.clear();
    mallrecordcaozuo.clear();
    mallrecordgaotienumber.clear();
    mallrecordstart.clear();
    infile>>allrecordshu;
    for(int i=1;i<=allrecordshu;i++)
    {
        infile>>r;
        allrecord.push_back(r);
        mallrecordname.insert(make_pair(r.getName(),allrecord.size()-1));
        mallrecordnumber.insert(make_pair(r.getNumber(),allrecord.size()-1));
        mallrecordcaozuo.insert(make_pair(r.getCaozuo(),allrecord.size()-1));
        mallrecordgaotienumber.insert(make_pair(r.getGaotienumber(),allrecord.size()-1));
        mallrecordstart.insert(make_pair(r.getStarttime(),allrecord.size()-1));
    }
    infile.close();
}

void Getrecord::recordsave()
{
    ofstream outfile("2018212591马俊光的购票记录.txt",ios::out);
    if(!outfile)
    {
        cout<<"can't save record!"<<endl;
        return;
    }
    outfile<<allrecordshu<<endl;
    for(int i=0;i<allrecord.size();i++)
    outfile<<allrecord[i];
    outfile.close();
    //test
    /*
    cout<<allrecordshu<<endl;
    for(int i=0;i<allrecord.size();i++)
    cout<<allrecord[i];
    */
}

/*
int main()
{
    Getrecord r;
    return 0;
}
*/

class User
{
    string number,name,mima;
    int honest;   //诚信度
    int t;  //(ticketrecord)表示总操作(购票,退票,改签)记录数,方便导入记录
    vector<Record>record;  //个人购票记录
    multimap<Time,int>mrecord;
    multimap<Time,int>::iterator m;
public:
    User() {};
    ~User() {};
    User(string number1,string name1,string mima1,int honest1):number(number1),name(name1),mima(mima1),honest(honest1) {};
    string getNumber() {return number;}
    string getName() {return name;}
    string getMima() {return mima;}
    int getHonest() {return honest;}     //非零即一,因此也可以使用bool类型操作
    int getT() {return t;}
    void setNumber(string number1) {number=number1;}  //其实也没有必要,就算名字可以改,身份证信息也无法修改(这里存在的原因是为了删除用户,将身份证信息置为"0")
    void setName(string name1) {name=name1;}
    void setMima(string mima1) {mima=mima1;}
    void setHonest(int honest1) {honest=honest1;}
    void setT(int t1) {t=t1;}
    void setRecord(string username_) //不是去修改记录,只是修改记录中对应的人名(考虑到后面人名的修改),其实实际中用处不大
    {
        for(int k=0;k<t;k++)
        {
            record[k].setUsername(username_);
        }
    }
    void zengrecord(Record r) {record.push_back(r);}
    void getRecord();
    void totravel(Time t);   //待出行,这里通过购票记录进行对时间的比较操作,列车发车时间在传参时间之后的均为待出行信息
    int panduan(string number,string start,string end,Time t)  //该判断是建立在用户记录中全部为待出行车票的基础上,过期记录不存在用户记录中
    {
        int x=0,y=0;
        for(int i=0;i<record.size();i++)
        {
            if(record[i].getGaotienumber()==number&&record[i].getStartsite()==start&&record[i].getEndsite()==end&&record[i].getStarttime()==t)
            {
                if(record[i].getCaozuo()=="购票")
                    x++;  //确实是买了
                if(record[i].getCaozuo()=="退票"||record[i].getCaozuo()=="改签")  //实际生活改签票不可退
                    y++;  //但是已经退过或者改签了
            }
        }
        if(x<=y)   //其实这里不会存在x<y的情况,只有等于
            return -1;
        if(x>y)
            return 1;
    }
    friend istream& operator>> (istream&,User&u);
    friend ostream& operator<< (ostream&,const User&u);
};

void User::getRecord()
{
    for(int i=0;i<record.size();i++)
        cout<<record[i].getCaozuo()<<" "<<record[i].getGaotienumber()<<" "<<record[i].getGaotiename()<<" "<<record[i].getStartsite()<<" "<<record[i].getStarttime()<<" "<<record[i].getEndsite()<<" "<<record[i].getEndtime()<<" "<<record[i].getS()<<endl;
}

void User::totravel(Time t)
{
    //对时间的比较,重载时间的"<"
    for(m=mrecord.begin();m!=mrecord.end();m++)
    {
        int i=m->second;
        if(m->first<t);    //所有的票与登陆日期进行比较,当票比登陆日期小时,证明该票已过期,不再显示
        else
            cout<<record[i].getCaozuo()<<" "<<record[i].getGaotienumber()<<" "<<record[i].getGaotiename()<<" "<<record[i].getStartsite()<<" "<<record[i].getStarttime()<<" "<<record[i].getEndsite()<<" "<<record[i].getEndtime()<<" "<<record[i].getS()<<endl;
    }
}

istream& operator>>(istream&in,User&u)
{
    in>>u.number>>u.name>>u.mima>>u.honest>>u.t;
    Record r;
    u.record.clear();
    u.mrecord.clear();
    for(int i=1;i<=u.t;i++)
    {
        in>>r;
        u.record.push_back(r);
        u.mrecord.insert(make_pair(r.getStarttime(),u.record.size()-1));
    }
    return in;
}

ostream& operator<< (ostream&out,const User&u)
{
    out<<u.number<<" "<<u.name<<" "<<u.mima<<" "<<u.honest<<" "<<u.t<<endl;
    for(int i=0;i<u.record.size();i++)
    {
        out<<u.record[i];
    }
    return out;
}

/*
int main()
{
    User u;
    cin>>u;
    cout<<u;
    u.getRecord();
    Time t;
    int n=3;
    while(n--)
    {
        cin>>t;
        u.totravel(t);
    }
    return 0;
}
*/

//test
class Guanliop:public Gaotieop,public Getrecord
{
    vector<User>user;
    //vector<Record>record;
    int usershu;
    multimap<string,int>musername;
    multimap<string,int>musernumber;
    multimap<string,int>::iterator mm,mm1,mm2;
public:
    Guanliop() {uload();};
    ~Guanliop() {usave();};
    int getUsershu() {return usershu;}
    void setUsershu(int usershu1) {usershu=usershu1;}
    void uload();  //只导入用户信息
    void usave();
    //对于高铁车次信息的查找与操作调用Gaotieop基类
    void usersearch();
    void userzeng();
    void userdelete();
    void userset();
    void alluser();
    virtual void getRecord(); //对于记录的查询方式多种多样
};

void Guanliop::uload()
{
    ifstream infile("2018212591马俊光的用户信息.txt",ios::in);
    if(!infile)
    {
        cout<<"can't load user!"<<endl;
        return;
    }
    User u;
    user.clear();
    musername.clear();
    musernumber.clear();
    infile>>usershu;
    for(int i=1;i<=usershu;i++)
    {
        infile>>u;
        user.push_back(u);
        musername.insert(make_pair(u.getName(),user.size()-1));
        musernumber.insert(make_pair(u.getNumber(),user.size()-1));
    }
    infile.close();
}

void Guanliop::usave()
{
    ofstream outfile("2018212591马俊光的用户信息.txt",ios::out);
    if(!outfile)
    {
        cout<<"can't save user!"<<endl;
        return;
    }
    outfile<<usershu<<endl;
    for(int i=0;i<user.size();i++)
    {
        if(user[i].getNumber()!="0")
        outfile<<user[i]<<endl;
    }
    outfile.close();
    //test
    /*
    cout<<usershu<<endl;
    for(int i=0;i<user.size();i++)
    {
        cout<<user[i]<<endl;
    }
    */
}

/*
int main()
{
    Guanliop op;
    //管理端对列车信息的实现:
    /*
    op.allgaotie();
    op.gaotiesearch();
    op.gaotieset();
    op.gaotiedelete();
    op.allgaotie();
    op.gaotiezeng();
    op.allgaotie();
    op.gaotiesearch();

    op.uload();
    op.usave();
    return 0;
}
*/

void Guanliop::usersearch()
{
    string caozuo,caozuo_;
    cin>>caozuo;
    if(caozuo=="name")
    {
        cin>>caozuo_;
        mm=musername.find(caozuo_);
        if(mm!=musername.end())
        {
            mm1=musername.lower_bound(caozuo_);
            mm2=musername.upper_bound(caozuo_);
            for(mm=mm1;mm!=mm2;mm++)  //人名可能重复,身份证号不会
            cout<<user[mm->second];
        }
        else cout<<"No user!"<<endl;
    }
    else if(caozuo=="number")
    {
        cin>>caozuo_;
        mm=musernumber.find(caozuo_);
        if(mm!=musernumber.end())
            cout<<user[mm->second];
        else cout<<"No user!"<<endl;
    }
}

void Guanliop::alluser()
{
    string caozuo;
    cin>>caozuo;
    if(caozuo=="name")
    {
        for(mm=musername.begin();mm!=musername.end();mm++)
            cout<<user[mm->second]<<endl;
    }
    else if(caozuo=="number")
    {
        for(mm=musernumber.begin();mm!=musernumber.end();mm++)
            cout<<user[mm->second]<<endl;
    }
}

void Guanliop::userzeng()
{
    User u;
    cin>>u;
    user.push_back(u);
    musername.insert(make_pair(u.getName(),user.size()-1));
    musernumber.insert(make_pair(u.getNumber(),user.size()-1));
    setUsershu(getUsershu()+1);
}

void Guanliop::userdelete()
{
    string caozuo;
    cin>>caozuo;
    mm=musernumber.find(caozuo);
    if(mm!=musernumber.end())
    {
        int i=mm->second;
        cout<<user[mm->second];
        user[i].setNumber("0");
        setUsershu(getUsershu()-1);
        musernumber.erase(mm);
        mm1=musername.lower_bound(user[i].getName());
        mm2=musername.upper_bound(user[i].getName());
        for(mm=mm1;mm!=mm2;mm++)  //人名可能重复,身份证号不会
        {
            if(mm->second==i)
            {
                musername.erase(mm);
                break;
            }
        }
    }
    else cout<<"No user!"<<endl;
}

void Guanliop::userset()
{
    string caozuo,caozuo_;
    int honest_;
    cin>>caozuo;
    mm=musernumber.find(caozuo);
    if(mm!=musernumber.end())
    {
        int i=mm->second;
        cout<<user[mm->second];
        //由于修改内容较少,不再设置循环
        cin>>caozuo_;
        if(caozuo_=="name")
        {
            mm1=musername.lower_bound(user[i].getName());
            mm2=musername.upper_bound(user[i].getName());
            for(mm=mm1;mm!=mm2;mm++)
            if(mm->second==i)
            {
                musername.erase(mm);
                break;
            }
            cin>>caozuo_;
            user[i].setName(caozuo_);
            //对用户所做记录的票都应更改名字
            user[i].setRecord(caozuo_);
            /*
            for(int k=0;k<allrecordshu;k++)
            {
                if(allrecord[k].getNumber()==caozuo)
                allrecord[k].setUsername(caozuo_);
            }
            */
            //当我们不导入记录文件时使用上面的for循环不可以,或者将allrecordshu改为allrecord.size()
            vector<Record>::iterator it;
            for(it=allrecord.begin();it!=allrecord.end();it++)
            {
                if(it->getNumber()==caozuo)
                it->setUsername(caozuo_);
            }
            musername.insert(make_pair(user[i].getName(),i));
        }
        else if(caozuo_=="number");  //现实中名字可以改,身份证信息是不变的
        else if(caozuo_=="mima");   //管理端是不能涉及更改用户端密码信息的
        else if(caozuo_=="honest")  //管理端是有权拉黑用户的,这不是对用户进行注销,只是限制用户买票、退票、改签等操作,用户信息依然保留,可以进行查票
        {
            cin>>honest_;
            user[mm->second].setHonest(honest_);
        }
    }
    else cout<<"No user!"<<endl;
}

void Guanliop::getRecord()
{
    string caozuo;  //对于整体的显示,也可以显示单个人的
    cin>>caozuo;
    multimap<string,int>::iterator x,x1,x2;
    multimap<Time,int>::iterator xx;
    if(caozuo=="username")
    {
        for(x=mallrecordname.begin();x!=mallrecordname.end();x++)
            cout<<allrecord[x->second]<<endl;
    }
    else if(caozuo=="usernumber")
    {
        for(x=mallrecordnumber.begin();x!=mallrecordnumber.end();x++)
            cout<<allrecord[x->second]<<endl;
    }
    else if(caozuo=="fangshi")
    {
        for(x=mallrecordcaozuo.begin();x!=mallrecordcaozuo.end();x++)
            cout<<allrecord[x->second]<<endl;
    }
    else if(caozuo=="gaotienumber")
    {
        for(x=mallrecordgaotienumber.begin();x!=mallrecordgaotienumber.end();x++)
            cout<<allrecord[x->second]<<endl;
    }
    else if(caozuo=="starttime1")
    {
        for(xx=mallrecordstart.begin();xx!=mallrecordstart.end();xx++)
            cout<<allrecord[xx->second]<<endl;
    }
    else if(caozuo=="starttime2")
    {
        for(xx=(--mallrecordstart.end());xx!=mallrecordstart.begin();xx--)
            cout<<allrecord[xx->second]<<endl;
        cout<<allrecord[mallrecordstart.begin()->second]<<endl;
    }
    else if(caozuo=="user")
    {
        string caozuo_;
        cin>>caozuo_;
        //if(caozuo_=="name");  //只是对单个用户,考虑到重名等其他问题,这里只按身份证对个人购票信息进行查询
        //else if(caozuo_=="numer")
        //{
            //cin>>caozuo_;
            x=mallrecordnumber.find(caozuo_);
            if(x!=mallrecordnumber.end())
            {
                x1=mallrecordnumber.lower_bound(caozuo_);
                x2=mallrecordnumber.upper_bound(caozuo_);
                for(x=x1;x!=x2;x++)
                    cout<<allrecord[x->second]<<endl;
            }
            else cout<<"无该用户购票信息"<<endl;
        //}
    }
}

/*
int main()
{
    Guanliop op;
    op.uload();
    //op.alluser();
    /*
    op.usersearch();
    op.userzeng();
    op.alluser();
    op.userdelete();
    op.alluser();
    op.userset();

    /*
    op.userset();
    op.usersearch();
    op.alluser();

    int n=3;
    while(n--)
    op.getRecord();
    op.usave();
    return 0;
}
*/

class Userop:public Gaotieop,public Getrecord
{
    vector<User>user;
    int usershu;   //只是方便读取与写入文件,对其不作查询修改(无该权限)
    //vector<Record>record; //用户虽然没有查询记录的权限,但是需要压入每一次用户自己的记录,觉得没必要写继承记录,因为并不对记录进行查询,只是增加新记录而已
    int ii;    //表示用户ii
    Time tt;   //表示登录时间tt
public:
    Userop() {};
    Userop(Time tt1,int ii1):tt(tt1),ii(ii1) {uload();};
    ~Userop() {usave();};
    void uload();
    void usave();
    //getRecord()多态只显示用户记录,与管理端不同
    virtual void getRecord() {user[ii].getRecord();};  //对于记录的查询方式多种多样
    void totravel_() {user[ii].totravel(tt);}
    void gaotiefind_();
    void buy();
    void back();
    void gaiqian();
    //类似于以下三项等对高铁车次信息的修改不调用,或像下面这样只写空函数进行同名覆盖
    void gaotiezeng();
    void gaotieset();
    void gaotiedelete();
};

//文件的导入与保存都只是对用户作操作(基本同管理端对用户的导入与保存类似)(派生类)(对于高铁信息以及记录信息直接在基类就进行了读取与保存操作)
void Userop::uload()
{
    ifstream infile("2018212591马俊光的用户信息.txt",ios::in);
    if(!infile)
    {
        cout<<"can't load user!"<<endl;
        return;
    }
    User u;
    user.clear();
    infile>>usershu;
    for(int i=1;i<=usershu;i++)
    {
        infile>>u;
        user.push_back(u);
    }
    infile.close();
}

void Userop::usave()
{
    ofstream outfile("2018212591马俊光的用户信息.txt",ios::out);
    if(!outfile)
    {
        cout<<"can't save user!"<<endl;
        return;
    }
    outfile<<usershu<<endl;
    for(int i=0;i<user.size();i++)
    {
        if(user[i].getNumber()!="0")
        outfile<<user[i]<<endl;
    }
    outfile.close();
    //test
    /*
    cout<<usershu<<endl;
    for(int i=0;i<user.size();i++)
    {
        cout<<user[i]<<endl;
    }
    */
}

/*
int main()
{
    Userop op;
    op.uload();
    op.usave();
    return 0;
}
*/

/*
int main()
{
    //Time t;
    //int i;
    //cin>>t>>i;
    //Userop op(t,i);
    Userop op;
    op.uload();
    //op.gaotiesearch();    //(管理端的,用户无访问权限)
    //对于车次信息的查询(用户端可按发车时间或按耗时最短的筛选方式进行查询)
    string start,end;
    Time t;
    cin>>start>>end>>t;
    op.gaotiefind(start,end,t);
    op.usave();
    return 0;
}
*/

void Userop::gaotiefind_()
{
    string start,end;
    cin>>start>>end;
    gaotiefind(start,end,tt);
}

//在买之前应该查到了关于车次的相关信息
void Userop::buy()
{
    if(user[ii].getHonest()==0)
    {
        cout<<"对不起,您的诚信度存在问题,无法购票!"<<endl;
        return;
    }
    string number;      //输入车号
    string start,end;  //起始站点
    Time t;   //输入要买的票的发车时间
    cin>>number>>start>>end>>t;
    if(t<tt) cout<<"您输入的时间有误,购票失败!"<<endl;  //判断所购票的时间是否在登录时间之后,防止买过期票(其实查询已经查不到过期票了,防止用户输入错误)
    else{
    m=gaotienumber.find(number);
    if(m!=gaotienumber.end())
    {
        if(vgaotie[m->second].getNumber()!="0")   //列车存在
        {
            //在该列车上(某一列车)上写一函数进行买票(退票),需要导入的有站点信息,所买票的时间
            //对Gaotie类作处理(某一车次)
            int x=vgaotie[m->second].buy(start,end,t);  //导入后进行判断,看输入信息是否正确或是否存在余票
            //关于站点信息的输入或者是时间的输入问题以及是否有余票在函数中进行显示
            //购票成功返回1,反之返回-1
            if(x==1)
            {
                cout<<"恭喜您已成功买到该车票"<<endl;
                //对记录执行操作
                //首先获取记录的相关信息,这里还有到站时间与初始里程差待获取
                Time endtime_=vgaotie[m->second].endtime(end);
                int sss=vgaotie[m->second].s(start,end);
                Record r(user[ii].getName(),user[ii].getNumber(),"购票",number,vgaotie[m->second].getName(),start,t,end,endtime_,sss);
                user[ii].zengrecord(r);
                user[ii].setT(user[ii].getT()+1);
                allrecord.push_back(r);
                allrecordshu++;
            }
            if(x==-1)
                cout<<"购票失败!"<<endl;
        }
        else cout<<"对不起,该列车已停运,购票失败!"<<endl;
        //一般不会有上面的情况,除非用户先查询到,这时又没有人买票,管理员又刚好将该列车删掉(停运)
    }
    else cout<<"列车信息输入有误,购票失败!"<<endl;
    }
}

void Userop::back()
{
    if(user[ii].getHonest()==0)
    {
        cout<<"对不起,您的诚信度存在问题,退票失败!"<<endl;
        return;
    }
    string number;      //输入车号
    string start,end;  //起始站点
    Time t;   //输入要退的票的发车时间
    cin>>number>>start>>end>>t;
    if(t<tt) cout<<"您输入的时间有误,退票失败!"<<endl;  //判断所退票的时间是否在登录时间之后,防止退过期票
    else{
    //退票改签之前首先应确定用户是否有初始票(其实出票信息的判断基本就确定了能否退票)
    int  panduan_=user[ii].panduan(number,start,end,t);
    if(panduan_==-1) {cout<<"对不起,您没有该车票信息待出行,无法退票!"<<endl; return;}
    m=gaotienumber.find(number);
    if(m!=gaotienumber.end())
    {
        if(vgaotie[m->second].getNumber()!="0")   //列车存在
        {
            int x=vgaotie[m->second].back(start,end,t);  //导入后进行判断,看输入信息是否正确
            //退票成功返回1,反之返回-1
            if(x==1)
            {
                cout<<"恭喜您已成功退票!"<<endl;
                Time endtime_=vgaotie[m->second].endtime(end);
                int sss=vgaotie[m->second].s(start,end);
                Record r(user[ii].getName(),user[ii].getNumber(),"退票",number,vgaotie[m->second].getName(),start,t,end,endtime_,sss);
                user[ii].zengrecord(r);
                user[ii].setT(user[ii].getT()+1);
                allrecord.push_back(r);
                allrecordshu++;
            }
            if(x==-1)
                cout<<"退票失败!"<<endl;
        }
        else cout<<"对不起,该列车已停运,退票失败!"<<endl;  //其实是不存在这种情况的,只要用户买了票,管理端是无法停运该车次的
    }
    else cout<<"列车信息输入有误,退票失败!"<<endl;
    }
}

void Userop::gaiqian()  //去12306搜了一下,改签是指不变更起始点对车票的乘车日期,车次等进行修改,另外对于每一张票仅办理一次改签手续,改签之后无法改签第二次,只能退票(这里暂不考虑)
{
    if(user[ii].getHonest()==0)
    {
        cout<<"对不起,您的诚信度存在问题,改签失败!"<<endl;
        return;
    }
    string number1,number2;      //输入车号
    string start,end;  //起始站点
    Time t1;   //输入改签之前的车票发车时间
    Time t2;   //输入改签之后的车票发车时间
    int x1,x2;  //判断
    cin>>start>>end;  //先输入起始站点(不变)
    cin>>number1>>number2;
    cin>>t1>>t2;
    if(t1<tt||t2<tt) {cout<<"您输入的时间有误,改签失败!"<<endl; return;}
    else{
    int  panduan_=user[ii].panduan(number1,start,end,t1);
    if(panduan_==-1) {cout<<"对不起,您没有该车票信息待出行,无法改签!"<<endl; return;}
    m=gaotienumber.find(number2);  //先看要改的车还有没有票
    if(m!=gaotienumber.end())
    {
        if(vgaotie[m->second].getNumber()!="0")   //列车存在
        {
            x1=vgaotie[m->second].gaiqianbu(start,end,t2);  //导入后进行判断,看输入信息是否正确
            //可以改签返回1,反之返回-1
            if(x1==-1) {cout<<"改签失败!"<<endl; return;}
        }
        else {cout<<"对不起,该列车已停运,改签失败!"<<endl; return;} //其实是不存在这种情况的,只要用户买了票,管理端是无法停运该车次的
    }
    else {cout<<"列车信息输入有误,改签失败!"<<endl; return;}
    }
    m=gaotienumber.find(number1);    //准备退票
    if(m!=gaotienumber.end())
    {
        if(vgaotie[m->second].getNumber()!="0")   //列车存在
        {
            x2=vgaotie[m->second].gaiqiantui(start,end,t1);  //导入后进行判断,看输入信息是否正确
            //可以改签返回1,反之返回-1
            if(x2==-1) {cout<<"改签失败!"<<endl; return;}
        }
        else {cout<<"对不起,该列车已停运,改签失败!"<<endl; return;} //其实是不存在这种情况的,只要用户买了票,管理端是无法停运该车次的
    }
    else {cout<<"列车信息输入有误,改签失败!"<<endl; return;}
    if(x1==1&&x2==1)  //可改签,走到这一步前面是没有任何改签失败的提示信息的
    {
        m=gaotienumber.find(number2);
        x1=vgaotie[m->second].buy(start,end,t2);
        if(x1==-1) //人流量过大,短短一瞬间没票了,其实在该程序中不会出现该问题
        {cout<<"对不起,改签失败!"<<endl; return;}
        m=gaotienumber.find(number1);
        x2=vgaotie[m->second].back(start,end,t1);
        cout<<"恭喜您改签成功!"<<endl;
        //对记录执行操作
        m=gaotienumber.find(number1);
        Time endtime_=vgaotie[m->second].endtime(end);
        int sss=vgaotie[m->second].s(start,end);
        Record r1(user[ii].getName(),user[ii].getNumber(),"改签",number1,vgaotie[m->second].getName(),start,t1,end,endtime_,sss);
        user[ii].zengrecord(r1);
        allrecord.push_back(r1);
        user[ii].setT(user[ii].getT()+1);
        allrecordshu++;
        m=gaotienumber.find(number2);
        endtime_=vgaotie[m->second].endtime(end);
        sss=vgaotie[m->second].s(start,end);
        Record r2(user[ii].getName(),user[ii].getNumber(),"改签",number2,vgaotie[m->second].getName(),start,t2,end,endtime_,sss);
        user[ii].zengrecord(r2);
        allrecord.push_back(r2);
        user[ii].setT(user[ii].getT()+1);
        allrecordshu++;
    }
}

class Login
{
    vector<User>user;
    multimap<string,int>usernumber;
    multimap<string,int>::iterator m;
    int usershu;   //只是方便读取文件,对其不作任何查询或修改
public:
    Login() {uload();};
    ~Login() {};
    void uload();
    void login();
};

void Login::uload()
{
    ifstream infile("2018212591马俊光的用户信息.txt",ios::in);
    if(!infile)
    {
        cout<<"can't load user!"<<endl;
        return;
    }
    User u;
    user.clear();
    usernumber.clear();
    infile>>usershu;
    for(int i=1;i<=usershu;i++)
    {
        infile>>u;
        user.push_back(u);
        usernumber.insert(make_pair(u.getNumber(),user.size()-1));
    }
    infile.close();
}

void Login::login()
{
    string number,mima;
    cin>>number>>mima;
    if(number=="小光123"&&mima=="xiaoguang")
    {
        Guanliop op;
        //执行管理操作

        //test(部分测试)
        op.allgaotie();
        op.gaotiezeng();
        op.allgaotie();
        op.gaotieset();
        op.allgaotie();
        op.gaotiedelete();
        op.allgaotie();
        //gaotiesearch(string start1,string end1);  //需传参
        //gaotiefind(string start1,string end1,Time t1);
        op.alluser();
        op.usersearch();
        op.userzeng();
        op.userset();
        op.userdelete();
        op.alluser();
        op.getRecord();
    }
    else
    {
        m=usernumber.find(number);
        if(m!=usernumber.end())
        {
            int n=0;
            while(1)
            {
                if(user[m->second].getMima()==mima)
                {
                    cout<<"welcome back!"<<endl;
                    cout<<"请输入登录时间:"<<endl;
                    Time t;
                    cin>>t;
                    //cout<<user[m->second];  //多余记录不再输出,密码也不再显示
                    cout<<user[m->second].getNumber()<<" "<<user[m->second].getName()<<" "<<"诚信度: "<<user[m->second].getHonest()<<endl;
                    user[m->second].totravel(t);
                    Userop op(t,m->second);
                    //执行用户操作

                    //test(部分测试)
                    cout<<endl;
                    op.getRecord();
                    op.gaotiefind_();
                    op.gaotiefind_();
                    op.buy();
                    op.buy();
                    op.back();
                    op.gaiqian();
                    op.gaotiefind_();
                    op.totravel_();
                    break;
                }
                else
                {
                    n++;
                    cout<<"密码错误,请重新输入!"<<endl;
                }
                if(n==3) break;
                cin>>mima;
            }
        }
        else cout<<"No user!"<<endl;
    }
}

int main()
{
    ios::sync_with_stdio(false);
    Login ll;
    ll.login();
    return 0;
}

需要的文件:

2018212591马俊光的用户信息.txt
4
2018212591 马俊光 001 1 5
马俊光 2018212591 购票
G100 和谐号 北京站 2019 6 1 7 0 上海站 2019 6 1 12 0 1000
马俊光 2018212591 改签
G100 和谐号 北京站 2019 6 1 7 0 上海站 2019 6 1 12 0 1000
马俊光 2018212591 改签
G101 建设号 北京站 2019 6 2 7 0 上海站 2019 6 2 11 0 1000
马俊光 2018212591 购票
G101 建设号 北京站 2019 6 2 7 0 上海站 2019 6 2 11 0 1000
马俊光 2018212591 购票
G100 和谐号 北京站 2019 6 1 7 0 上海站 2019 6 1 12 0 1000

2018212598 黄敬中 002 1 1
黄敬中 2018212598 购票
G100 和谐号 北京站 2019 6 1 7 0 上海站 2019 6 1 12 0 1000

2018212591 常凯 003 1 1
常凯 2018212591 购票
G101 建设号 北京站 2019 6 2 7 0 泰山站 2019 6 1 8 30 200

2018212584 孙林 004 1 1
孙林 2018212584 购票
G101 建设号 北京站 2019 6 2 7 0 泰山站 2019 6 1 8 30 200

2018212591马俊光的车次信息.txt
4
G100 和谐号 1000 2
001 北京站 0 2019 6 1 7 0 2019 6 1 7 0 997
002 上海站 1000 2019 6 1 12 0 2019 6 1 12 0 1000
G101 建设号 1000 3
001 北京站 0 2019 6 2 7 0 2019 6 2 7 0 797
002 泰山站 200 2019 6 2 8 30 2019 6 2 8 40 797
003 上海站 1000 2019 6 2 11 0 2019 6 2 11 0 800
G103 和谐号1 1000 10
001 北京南 0 2019 6 29 6 53 2019 6 29 6 53 11
002 沧州西 100 2019 6 29 7 44 2019 6 29 7 54 11
003 济南西 200 2019 6 29 8 41 2019 6 29 8 45 11
004 泰安 300 2019 6 29 9 2 2019 6 29 9 4 11
005 枣庄 400 2019 6 29 9 42 2019 6 29 9 44 11
006 徐州东 500 2019 6 29 10 2 2019 6 29 10 4 11
007 滁州 600 2019 6 29 11 5 2019 6 29 11 12 11
008 南京南 700 2019 6 29 11 30 2019 6 29 11 34 11
009 无锡东 800 2019 6 29 12 17 2019 6 29 12 19 11
010 上海虹桥 1000 2019 6 29 12 48 2019 6 29 12 48 11
G5 和谐号2 1000 5
001 北京南 0 2019 6 30 7 0 2019 6 30 7 0 20
002 天津南 150 2019 6 30 7 31 2019 6 30 7 33 20
003 济南西 200 2019 6 30 8 30 2019 6 30 8 32 20
004 南京南 700 2019 6 30 10 30 2019 6 30 10 32 20
005 上海 1000 2019 6 30 11 40 2019 6 30 11 40 20
2018212591马俊光的购票记录.txt
8
马俊光 2018212591 购票
G100 和谐号 北京站 2019 6 1 7 0 上海站 2019 6 1 12 0 1000
马俊光 2018212591 改签
G100 和谐号 北京站 2019 6 1 7 0 上海站 2019 6 1 12 0 1000
马俊光 2018212591 改签
G101 建设号 北京站 2019 6 2 7 0 上海站 2019 6 2 11 0 1000
马俊光 2018212591 购票
G101 建设号 北京站 2019 6 2 7 0 上海站 2019 6 2 11 0 1000
马俊光 2018212591 购票
G100 和谐号 北京站 2019 6 1 7 0 上海站 2019 6 1 12 0 1000
黄敬中 2018212598 购票
G100 和谐号 北京站 2019 6 1 7 0 上海站 2019 6 1 12 0 1000
常凯 2018212555 购票
G101 建设号 北京站 2019 6 2 7 0 泰山站 2019 6 1 8 30 200
孙林 2018212584 购票
G101 建设号 北京站 2019 6 2 7 0 泰山站 2019 6 1 8 30 200

result与运行调试:

小光的12306:

对时间的操作判断:
在这里插入图片描述
对时间差的计算:
在这里插入图片描述
按计算器计算测试数据是没有问题的,到最后我们只返回分钟,输出数据不保留(一般只是用来计算站点间火车需要走过的时间以及在某一站点火车停留的时间,只返回分钟即可。

站点信息:
在这里插入图片描述

最后一项表示里程(从起点坐标为零计数)

对两站点距离差的计算:
在这里插入图片描述
对于列车信息:
在这里插入图片描述
这里也可输出站点停靠时间
对于列车车次信息的读取:
在这里插入图片描述
车次信息读取成功测试:
在这里插入图片描述
车次信息保存成功测试:
在这里插入图片描述
在这里插入图片描述
当然上述的重载输入输出还待操作使之保持一致

车次信息的查询:
在这里插入图片描述
输入起点终点来进行查找(这里直接输出的剩余票数)
(从起点至终点的一段路程中,票数最少的一段即为该段路程的剩余票数)

车次信息总览:
在这里插入图片描述
增加列车信息:
在这里插入图片描述
文件中保存后:
在这里插入图片描述
删除列车信息:
在这里插入图片描述
在这里插入图片描述
在显示所有列车信息时不显示已删除列车信息,在查询起始点时不显示列车信息,修改时也查询不到该列车信息,无法修改,保存时自动删除列车信息,同时总的列车数作出相应改变。

之前在操作时对于起始点终点站信息是直接输入站点名进行查询,实际生活中一般是不会使用站点编号的,因此这里管理端对站点信息的修改也是使用站点名来进行操作。

对列车信息进行修改(包括对列车name、number、seat、站点信息等修改):
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

上面的操作没有加入提示信息,真的难记啊,好多次自己操作因为没记住操作数出现了错误,最初只能通过看着代码输入实现功能,后来测了几次多了就背过自己写的操作了。

第二次再测(一开始看截图信息以为是错误的,其实是看错了,是没有问题的):
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
对于记录的重载输入输出(最后一项表示里程)
在这里插入图片描述
对于个人信息及其购票记录的重载输入输出
在这里插入图片描述
想通过所有的票与登陆日期进行比较,当票比登陆日期小时,证明该票已过期,不再显示:这里又要对时间进行重载小于号:
对时间重载小于号的测试:
在这里插入图片描述
之前也可以对时间进行相减,不过前面的时间减去后面的时间返回-1(该组测试进行了判断,当后面的日期可以减去前面时(前面小)输出值,否则不进行相减)
通过所有的票与登陆日期进行比较,当票比登陆日期小时,证明该票已过期,不再显示:
在这里插入图片描述
对于总购票记录的文件输入输出:
在这里插入图片描述
在这里插入图片描述
继承之后的列车实现与记录实现:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
对于用户信息的读取与保存(不读取高铁车次以及记录的相关信息):
在这里插入图片描述
在这里插入图片描述
对于用户信息的修改:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里还存在的一点是对用户改名后应对其所有操作的票作修改(名字作出相应更改)

下面是优化作出修改以后的(个人记录与总记录均作出修改):
在这里插入图片描述
文件保存测试:
在这里插入图片描述
文件保存结果:
在这里插入图片描述
在这里插入图片描述
对记录进行查询(管理端):
总览与单用户查询:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
下面补充一下用户端查询(这里是登陆之后的个人记录)(最后插入的)(用户是没有权限去访问别人的记录的)只能查询自己的记录:
在这里插入图片描述

用户对于文件的读取与保存:
在这里插入图片描述
在这里插入图片描述
不导入记录、用户信息,直接通过用户操作来继承查询途经起始站点的车次信息:
在这里插入图片描述
对于查票,用户与管理端是不一样的:
用户端只能查未出行的车次信息,当登录时用户端就会根据实际时间直接排除已过期的车次信息,没有权限去访问已过期的车次,但是用户端可以对这些车次信息进行筛选(按时间、按用时最少)(这里从始至终都没有考虑票价,先买上再说,假设乘客很着急或者很有钱不在乎票价或者不在乎几等座)如果添加几等座以及分别对应的票价就要考虑用户的身份信息,看用户是否是学生或儿童,然后是否要享受优惠,那样程序就会更加繁琐。
管理端应该具备用户端查询的所有功能,还应能查看访问之前的车次信息。

查询(用户端)(需导入时间,过期车次不显示)(按时间查询(发车最早),按耗时最短查询):

在这里插入图片描述

管理端(不受时间限制)(也可以像用户端一样筛选):
在这里插入图片描述
对其输出作优化,只显示起始站点:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
买票退票改签等操作需要某一用户与登录时间,构造函数用起来比较麻烦,这里先跳过,先用登录对其直接进行构造(先写出登录系统):

管理端未导入信息时登录:
在这里插入图片描述
用户端(导入用户信息后,否则没有办法识别,登不上的):
在这里插入图片描述
用户端进行买票、退票、改签的处理:
在这里插入图片描述
在这里插入图片描述
当没有购票信息恶意退票或改签时:
在这里插入图片描述
先去查用户是否买过票,再去判断是否可退或改签。
(实际生活中改签票改一次后不可退,程序中已实现)

文件中车次信息、用户信息、购票记录已完全实现,并能保存与读取:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
最后就是扩增数据(程序之前使用的数据量偏小一些),然后测试各项功能。
到此程序功能的所有实现过程基本演示完毕,接下来就是所有的功能统一测几次,只要不出问题,简单的12306就完成了。

设计体会与小结:

简单说一下设计系统具体的过程以及一些注意事项:‌
拿到一个设计的题目或者说一个项目的需求分析,首先要根据大体功能(需求分析)规划出基本的思路框架,然后一步步去思考实现,逐步地去优化,顺序可以从管理端到用户端,登录之类的可以放到最后写。(对于大体功能或者说需求分析,我总觉得这是设计系统最重要的一步,个人总是喜欢先列出来,比如先在纸上演示出来,然后弄成Word文档理清系统的具体框架,然后在写程序的时候作为参考,我觉得会让思路变得清晰,具体的可以参考最上面的12306大体框架。)

大体框架思路有了以后根据功能实现去设计‌数据结构,即数据的储存方式,例如查询方式根据查询功能需要设置多少组map或我们使用向量时是否要考虑使用下标去访问等,数据的存储往往根据我们要实现的功能去变换。另外数据的存储与操作方式还会影响程序的效率,是值得思考与关注的一点。

数据有了,我们就要进行数据的导入与保存,这时我们通常会采用‌重载输入输出,这是很严格的一个过程,必须保证数据与重载完全对应起来,否则可能无法导入数据,那这个程序就宣布作废了,一个没有数据、无法对数据进行操作的程序是没有意义的。很多时候重载会给我们带来很多便利,比如我们对时间类进行重载,作出时间差或者根据特定的值去推算时间等。但是又不得不说重载还是尽量少用一些, 因为有的时候万一错了可能会导致大的问题,比如上面提到的文件导入与保存。

另外重要的一点是‌继承中基类与派生类之间的关系,继承虽然好用,利于减少代码量,增强可读性,加强代码之间的联系等,但是稍有不慎就会出现错误,继承出现的错误有的时候可并非容易找,毕竟这是继承的特性,例如基类中save函数与析构函数之间的关系没有把握好,就会产生操作失败的问题,因为派生类操作完毕后进行了一次保存,但是基类析构时这时若再调用save函数,保存的是基类中未修改的数据,直接覆盖掉了之前的数据,相当于没有执行操作。当然这是继承中需要注意的问题,可能很多时候我们都知道这是错的,但是用到程序中数据一多就混乱了,这需要我们长期练习才行啊。

还有就是多态的使用,接触的比较少,原理是表面实现同一功能,实际呈现出不同界面,我们可以在登录界面部分写两个接口,分别使用基类指针指向管理端与用户端,实现看似相同但实际不同的功能,对于12306来说管理端与用户端功能并不是很类似,也可以使用普通的登录调用函数去代替,相反之前的通讯录有两个界面(手机卡与手机)基本实现功能类似,更容易使用多态。

最后要说的很重要的一步是‌程序的调试:我们都不是天才,不可能一步写完所有程序直接运行通过,我们做系统的数据量太大,必须一个功能点进行一步调试,这样才不至于到最后写了一堆没有办法下手改的程序,如果我们哪一步没弄好就不要往下进行,因为这样容易出现连环的错误。我们刚刚对系统有一个初步了解,一定要多练习调程序,按步骤找错误,看具体是哪个函数有问题,慢慢改,慢慢来,到最后保证程序连续起来不会出问题。其实我觉得进步最快的时候就是调程序改代码,我们刚开始做,不怕错的多,相反这是件好事,会给我们以后调程序打下更坚实的基础。我们也可以尝试去帮同学调程序,这其实是一种乐趣,会体会到别人对于同一个问题不同的思路,会看到对于细节的处理的不同方式,当然这不是叫我们去抄袭,去帮别人写系统(在自己的程序没有进行完之前不要去参考别人的代码,不要去看网上的资料,要学会独立思考,将自己的思路写到代码里面,我觉得这才是我们能学到东西的前提)调程序这只是自我提升的一种途径,有的时候自己错的多了,看别人的程序就能一眼看出来别人的错误,看起来挺厉害的,其实这个过程挺心酸的,这不就是自己之前错的那些吗。

按照上面的思路来,一个简单的系统基本就可以做出来了,但是我们离社会对我们的要求还很远很远,不过我们已经大体掌握了设计系统的基本思路,接下来我们可以自己深入了解一些东西了。当然我们也不能急于求成,很多东西我们都要反复练习,一定要把基础打实。

学习之路道阻且长,当加倍努力才行,加油!

推荐阅读