杂例-面向对象小练习之学生一卡通消费统计

面向对象小练习之学生一卡通消费统计

原题链接
Code-ZH_Cpp/Study/Modify-2019JavaTest (Gitee)

总结

  1. map类型如果作为类的static成员,需要在类外定义

  2. 使用istringstream对象配合getline函数可以实现字符串的分割

    例如:Record类的构造函数(配合str_cast辅助函数)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    //辅助函数
    //用于将字符串转换到类型T数值的内联函数
    template<class T>
    inline T str_cast(const string& str) {
    istringstream cast(str);
    T result;
    cast >> result;
    return result;
    }
    //……
    Record::Record(const string& info) { //构造函数,使用包含所有信息的字符串
    istringstream is(info);
    string s_type,s_amount, s_location;
    getline(is, s_type, ' '); type = str_cast<int>(s_type);
    getline(is, s_amount, ' '); amount = str_cast<double>(s_amount);
    getline(is, s_location, ' '); location = str_cast<int>(s_location);
    }
  3. 适当地声明一些友元函数可以提高这些函数获取私有数据的速度

    例如:Record类中声明了

    1
    2
    friend ostream& operator<<(ostream& out, const Record& record);
    friend class Card; //便于Card类获取用卡类型和金额
  4. 如果在某一类的方法实现中使用了其他类的对象(或引用),应该在这个对象的类完整定义后再给出这一函数定义

    例如:本题中的Card类setRecord方法使用到了Student类的对象的引用,应该在Student类完整定义后再给出setRecord方法的定义

  5. 位于<algorithm>头文件中的sort方法:

    1
    2
    template <class _RanIt, class _Pr>
    sort(const _RanIt _First, const _RanIt _Last, _Pr _Pred); // order [_First, _Last), using _Pred

    第一个参数为容器要排序的起始位置对迭代器,第二个参数为终止位置的下一个位置对迭代器,第三个参数(可选)为排序要用的比较大小函数指针(默认升序)

    例如:找到存钱最多的学生中使用了辅助比较函数,以学生存钱为依据降序排列,取第一个元素即为所要找的学生

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //辅助比较函数,以学生存钱为依据降序排列
    bool cmp(const Student& st1, const Student& st2) {
    return (st1.stuCard.getSave() > st2.stuCard.getSave());
    }
    //保存存钱最多的学生到文本文件
    void SaveTop(vector<Student> Students_t, const string& fileName) {
    //这里是值传递,排序不会影响主调方的原来的顺序
    sort(Students_t.begin(), Students_t.end(), cmp);
    ofstream fout(fileName, ios_base::out);
    Students_t[0].show(fout);
    fout.close();
    }
  6. 为了使自定义的数据类型可以比较大小,可以为自定义的类重载比较运算符

    例如本题中,为使得学生类可以用一卡通消费金额作为默认的比较依据,为其重载了<运算符,使得在调用sort方法时可以不用提供排序用的函数:

    1
    2
    3
    4
    5
    6
    7
    //Student类中
    //重载<,用一卡通消费金额作为比较依据
    bool operator<(const Student& stu) const {
    return (this->stuCard.getSpend() < stu.stuCard.getSpend());
    }
    //对vector对象students排序时
    sort(Students.begin(), Students.end());

题目

详见Gitee链接

按以下要求编写程序

题目说明

给定三个文本文件,它们的格式及含义如下:

  1. Stddata.txt 存放了若干学生的基本信息,每个学生一行,各基本信息间以空格分隔

    (格式:14位学号 姓名 入学年份 专业 联系电话)

    例如: 20188374858604 宋和科 2018 电器工程 133279242

  2. Recorddata.txt 存放了一卡通的用卡情况,一张卡可以有若干消费记录,一行对应一条消费记录,消费记录的各信息间以空格分隔

    (格式:10位卡号 用卡类型 发生金额 发生地点编号)

    例如: 0000000007 0 28.72 2

    用卡类型有两种:0-表示存钱操作;1-表示消费操作

    发生地点变化目前有4个点,编号0~3

  3. StdCard.txt 存放了学生持有的一卡通的状况,学生与一卡通间一一对应,每个学生一行,各基本信息间以空格分隔

    (格式:14位学号 10位一卡通卡号)

    例如:20181028947850 0000000005

    表示学号是20181028947850的学生使用的一卡通卡号为0000000005

题目要求

  1. 合理设计:类内部的数据及方法,以及类与类之间的关系。

  2. 完成如下一些功能:

    (1)从消费情况文件Recorddata.txt文件中加载所有用卡信息,从Stddata.txt文件中读取所有学生信息,从学生持卡文件StdCard.txt中读取学生持卡状况,完成数据的导入和整合。

    (2)统计每位学生一卡通的消费情况(注:存钱类操作不关注),并按消费金额的非降序对学生进行排序,将排序后的学生基本信息、其对应的一卡通基本信息和消费总金额发送到屏幕显示。

    (3)将存钱最多的学生的基本信息、其对应的一卡通信息,以及他的详细的用卡信息以文本文件的形式保存到SaveTop.txt文件。

使用搜索:谷歌必应百度