• 【黑马程序员】STL实战--演讲比赛管理系统


    演讲比赛管理系统

    需求说明

    比赛规则

    在这里插入图片描述

    程序功能

    在这里插入图片描述

    创建管理类

    功能描述

    • 提供菜单界面与用户交互

    • 对演讲比赛流程进行控制

    • 与文件的读写交互

    创建演讲比赛管理类

    • 新建speechManager.hpp
    #pragma once
    
    #include 
    
    using namespace std;
    
    // 设计演讲比赛类
    class SpeechManager {
    public:
        // 构造函数    
        SpeechManager();
        // 析构函数   
        ~SpeechManager();
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 新建speechManager.cpp
    #include "speechManager.h"
    
    // 构造函数
    SpeechManager::SpeechManager() {
    
    }
    
    // 析构函数
    SpeechManager::~SpeechManager() {
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    菜单功能

    • 功能描述:与用户的交互界面

    添加菜单成员函数声明

    • speechManager.hpp中添加展示菜单成员函数声明
        // 菜单功能
        void showMenu();
    
    • 1
    • 2

    菜单成员函数实现

    • speechManager.cpp中实现成员函数
    // 菜单功能
    void SpeechManager::showMenu() {
        cout << "*************************************" << endl;
        cout << "*******欢迎使用演讲比赛管理系统******" << endl;
        cout << "************1.开始演讲比较***********" << endl;
        cout << "************2.查看往届记录***********" << endl;
        cout << "************3.情况比赛记录***********" << endl;
        cout << "************4.退出比赛程序***********" << endl;
        cout << "*************************************" << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    菜单功能测试

    • 菜单功能测试,新建main.cpp
    #include "speechManager.hpp"
    
    int main() {
        SpeechManager sm;
        sm.showMenu();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 运行结果

    在这里插入图片描述

    退出功能

    添加退出功能声明

    • speechManager.hpp中添加退出成员函数声明
        // 退出功能
        void Logout();
    
    • 1
    • 2

    退出成员函数实现

    • speechManager.cpp中实现退出成员函数
    // 退出功能
    void SpeechManager::Logout() {
        cout << "即将退出系统,再见" << endl;
        exit(0);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    退出功能测试

    • 退出功能测试,在main.cpp
    #include "speechManager.hpp"
    
    int main() {
        int choice;
        SpeechManager sm;
        while (true) {
            sm.showMenu();
            cout << "请输入你要使用的功能" << endl;
            cin >> choice;
            switch (choice) {
                case 1: //1.开始演讲比较
                    break;
                case 2: //2.查看往届记录
                    break;
                case 3: //3.清空比赛记录
                    break;
                case 4: //4.退出比赛程序
                    sm.Logout();
                    break;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 运行结果

    在这里插入图片描述

    演讲比赛功能

    功能分析

    • 比赛流程分析

      • 抽签–>开始演讲比赛–>显示第一轮比赛结果

      • 抽签–>开始演讲比赛–>显示前三名结果–>保存分数

    创建选手类

    • 选手类中包括属性:选手姓名、分数

    • 新建speaker.hpp

    #pragma once
    
    #include 
    #include 
    
    using namespace std;
    
    // 选手类
    class Speaker {
    public:
        string name;        // 姓名
        double score[2];    // 最多有两轮分数
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    比赛

    成员属性添加
    • speechManager.hpp中添加
    #include 
    #include 
    #include "speaker.hpp"
    
    // 类成员属性添加
        // 比赛选手容器 12人
        vector<int> v1;
        // 第一轮晋级选手容器 6人
        vector<int> v2;
        // 前三名容器 3人
        vector<int> v3;
        // 存放编号,以及队形的具体选手容器
        map<int, Speaker> mSpeaker;
        // 记录比赛轮数
        int mIndex;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    初始化属性
    • speechManager.hpp中添加初始化属性成员函数声明
        // 初始化成员属性
        void initSpeech();
    
    • 1
    • 2
    • speechManager.cpp中实现初始化属性成员函数,并在构造函数中调用
    // 初始化成员属性
    void SpeechManager::initSpeech() {
        // 初始确保容器为空
        this->v1.clear();
        this->v2.clear();
        this->v3.clear();
        this->mSpeaker.clear();
        // 初始比赛轮数
        this->mIndex = 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    创建选手
    • speechManager.hpp中添加创建选手成员函数声明
        // 创建选手
        void createSpeaker();
    
    • 1
    • 2
    • speechManager.cpp中实现创建选手成员函数,并在构造函数中调用
    // 创建选手
    void SpeechManager::createSpeaker() {
        string nameSeed = "ABCDEFGHIJKL";
        for (int i = 0; i < nameSeed.size(); i++) {
            string name = "选手";
            name += nameSeed[i];
            Speaker sp;
    
            sp.name = name;
            for (int i = 0; i < 2; i++) {
                sp.score[i] = 0;
            }
            // 12名选手编号
            this->v1.push_back(i + 10001);
            // 将选手放入到map中
            this->mSpeaker.insert(make_pair(i + 10001, sp));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 创建成员测试
    #include "speechManager.hpp"
    #include "speaker.hpp"
    
    int main() {
        SpeechManager sm;
        for (map<int, Speaker>::iterator it = sm.mSpeaker.begin(); it != sm.mSpeaker.end(); it++) {
            cout << it->first << "\t" << (it->second).name << "\t" << (it->second).score[0] << "\t" << (it->second).score[1]
                 << endl;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 运行结果

    在这里插入图片描述

    开始比赛成员函数
    • speechManager.hpp中添加开始比赛函数声明
        // 开始比赛
        void startSpeech();
    
    • 1
    • 2
    • speechManager.cpp中实现初始化属性成员函数,在开始比赛实现中先定义整体比赛的流程在慢慢实现,开始比赛功能在菜单中用户选择开始比赛时调用
    // 开始比赛
    void SpeechManager::startSpeech() {
        // 第一轮比赛
        // 1.抽签
    
        // 2.比赛
    
        // 3.显示晋级结果
    
        // 第二轮比赛
        // 1.抽签
    
        // 2.比赛
    
        // 3.显示最终结果
    
        // 4.保存分数
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    抽签
    • 抽签的本质就是打乱参赛者

    • speechManager.hpp中添加抽签函数声明

        // 抽签
        void drawLots();
    
    • 1
    • 2
    • speechManager.cpp中实现抽签函数,并在开始比赛的相应抽签流程位置调用
    // 抽签
    void SpeechManager::drawLots() {
        cout << "第" << this->mIndex << "轮比赛选手正在抽签" << endl;
        cout << "抽签后演讲顺序如下:" << endl;
        if (this->mIndex == 1) {
            // 第一轮抽签
            random_shuffle(this->v1.begin(), this->v1.end());
            printVector(this->v1);
        } else {
            // 第二轮抽签
            random_shuffle(this->v2.begin(), this->v2.end());
            printVector(this->v2);
        }
        cout << "------------------" << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 运行结果

    在这里插入图片描述

    比赛过程
    • 比赛过程为最重要部分,需要好好理解消化

    • 比赛流程梳理

      • 准备临时容器存放小组成绩,每处理完一组

      • 判断是第几轮比赛,选择对应的比赛选手

      • 遍历当前比赛选手,让10个评委调用随机函数去给每个选手打分

      • 得到分数后放到双端队列deque中进行降序排列,然后弹出头尾实现,去掉最高分和去掉最低分操作

      • 计算每个比赛者的总分和平均分,将打分数据放在降序排列的临时map容器中,每6个人为一组取前3名

      • 将取得的前3名放到下一轮要比赛的选手容器中,清空临时存放成绩的容器s

    • speechManager.hpp中添加比赛函数声明

        // 比赛
        void game();
    
    • 1
    • 2
    • speechManager.cpp中实现比赛函数,并在开始比赛的相应比赛流程位置调用
    // 比赛
    void SpeechManager::game() {
        cout << "------------第" << this->mIndex << "轮比赛开始了------------" << endl;
        // 准备临时容器存放小组成绩
        multimap<double, int, greater<double> > groupScore;
        // 记录人员个数,6人一组
        int num = 0;
        // vSrc 当前比赛选手容器
        vector<int> vSrc;
        if (this->mIndex == 1) {
            // 给v1容器的人打分
            vSrc = v1;
        } else {
            // 给v2容器的人打分
            vSrc = v2;
        }
        // 遍历所有选手进行比赛
        for (vector<int>::const_iterator it = vSrc.begin(); it != vSrc.end(); it++) {
            num++;
            // 10个评委打分
            deque<double> d;
            for (int i = 0; i < 10; i++) {
                // 随机一个600~1000的数,然后在除10
                // .f表示是一个小数
                double score = (rand() % 401 + 600) / 10.f;
                // cout << score << " ";
                d.push_back(score);
            }
            // 对分数进行降序排列
            sort(d.begin(), d.end(), greater<double>());
            // 去掉一个最高分
            d.pop_front();
            // 去掉一个最低分
            d.pop_back();
            // 累加总分
            double sum = accumulate(d.begin(), d.end(), 0.0f);
            // 计算平均分
            double avg = sum / (double) d.size();
            // 打印平均分
            // cout << "编号:" << *it << " 姓名:" << this->mSpeaker[*it].name << " 平均分:" << avg << endl;
            // 将平均分放到map容器中
            this->mSpeaker[*it].score[this->mIndex - 1] = avg;
            // 将打分数据放入到临时小组容器中
            groupScore.insert(make_pair(avg, *it));
            // 每6人取前三名
            if (num % 6 == 0) {
                cout << "第" << num / 6 << "小组比赛名次:" << endl;
                for (multimap < double, int, greater < double > > ::iterator it = groupScore.begin();
                    it != groupScore.end();
                it++) {
                    cout << "编号:" << it->second << " 姓名:" << this->mSpeaker[it->second].name << " 分数:" << it->first
                         << endl;
                }
                // 获取前三名,放入下一轮容器中
                int count = 0;
                for (multimap < double, int, greater < double > > ::iterator it = groupScore.begin();
                    it != groupScore.end() && count < 3;
                it++, count++) {
                    if (this->mIndex == 1) {
                        v2.push_back(it->second);
                    } else {
                        v3.push_back(it->second);
                    }
                }
                // 取完一次需要清空一次小组容器
                groupScore.clear();
            }
        }
        cout << "----------第" << this->mIndex << "轮比赛完毕------------" << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    显示晋级选手信息
    • speechManager.hpp中添加显示晋级选手函数声明
        // 显示晋级结果
        void showScore();
    
    • 1
    • 2
    • speechManager.cpp中实现显示晋级选手函数,并在开始比赛的相应比赛流程位置调用
    // 显示晋级结果
    void SpeechManager::showScore() {
        cout << "第" << this->mIndex << "轮晋级选手信息如下: " << endl;
        vector<int> v;
        if (this->mIndex == 1) {
            v = v2;
        } else {
            v = v3;
        }
        for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
            cout << "编号:" << *it << " 姓名:" << this->mSpeaker[*it].name << " 分数:"
                 << this->mSpeaker[*it].score[this->mIndex - 1] << endl;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    第二轮比赛
    • 第二轮比赛只需要将index++,然后在开始比赛函数的第二轮调用对应的封装好的函数即可

    • 开始比赛整体调用代码如下

    // 开始比赛
    void SpeechManager::startSpeech() {
        // 第一轮比赛
        // 1.抽签
        this->drawLots();
        // 2.比赛
        this->game();
        // 3.显示晋级结果
        this->showScore();
        this->mIndex++;
        // 第二轮比赛
        // 1.抽签
        this->drawLots();
        // 2.比赛
        this->game();
        // 3.显示最终结果
        this->showScore();
        // 4.保存分数
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 运行结果

    在这里插入图片描述
    在这里插入图片描述

    保存分数
    • speechManager.hpp中添加保存分数函数声明
        // 保存分数
        void saveScore();
    
    • 1
    • 2
    • speechManager.cpp中实现保存分数函数,并在开始比赛流程最后位置调用
    void SpeechManager::saveScore() {
        ofstream ofs;
        // 以追加输出的方式打开文件, csv中内容需要以,号分割
        ofs.open("speech.csv", ios::out | ios::app);
        // 将每个人的数据保存到文件
        for (vector<int>::iterator it = v3.begin(); it != v3.end(); it++) {
            ofs << *it << ","
                << mSpeaker[*it].score[1] << ",";
        }
        ofs << endl;
        // 关闭文件
        ofs.close();
        cout << "记录已保存" << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    查看记录

    从文件中加载记录
    • 文件不存在:提示直接返回

    • 文件存在但为空:先读一个字符看看是否读到了EOF

    • 文件存在不为空:

      • 先将读出的一个字符回写到文件中

      • 按行读取

      • 每读完一行按照逗号分割的方式将每一个编号和成绩的子字符串拿出来放入到临时vector中

      • 将一行vector中的内容放入往届成绩记录record容器中

    将加载的记录进行展示
    • 遍历记录容器,将加载的内容一行一行的进行展示
    功能实现
    • speechManager.hpp中添加对应的成员声明
        // 加载往届记录
        void loadRecord();
    
        // 查看往届记录
        void showRecord();
    
        // 记录比赛轮数
        int mIndex;
        // 文件是否为空标志
        bool fileIsEmpty;
        // 往届记录
        map<int, vector<string> > mRecord;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • speechManager.cpp中实现相应的功能,在构造函数部分调用加载历史记录,在用户交互位置提供查看历史记录功能
    // 查看分数
    void SpeechManager::loadRecord() {
        ifstream ifs;
        ifs.open("speech.csv", ios::in);
        if (!ifs.is_open()) {
            // cout << "文件不存在" << endl;
            ifs.close();
            return;
        }
        // 文件存在
        // 文件被清空过的情况先读一个字符判断文件是否为空
        char ch;
        ifs >> ch;
        if (ifs.eof()) {
            cout << "文件为空" << endl;
            this->fileIsEmpty = true;
            ifs.close();
            return;
        }
        // 文件存在且不为空
        this->fileIsEmpty = false;
        // 将读取的字符放回
        ifs.putback(ch);
        string data;
        // index用来记录是第几届的数据,默认是第0届
        int index = 0;
        // 一行一行读
        while (ifs >> data) {
            // cout << data << endl;
            // 10005,83.6125,10007,82.7375,10008,80.725,
            // 存放每一届记录的前三编号和分数值
            vector <string> v;
            // pos查找逗号位置,默认为没有查到
            int pos = -1;
            // 从哪开始查找
            int start = 0;
            while (true) {
                // 从头开始查,
                pos = data.find(",", start);
                if (pos == -1) {
                    break;
                }
                string tmp = data.substr(start, pos - start);
                v.push_back(tmp);
                // 下一次开始位置从上一个找到的逗号的下一个位置开始
                start = pos + 1;
            }
            // 将数据塞入往届成绩记录容器中
            this->mRecord.insert(make_pair(index, v));
            index++;
        }
        ifs.close();
    }
    
    // 查看往届记录
    void SpeechManager::showRecord() {
        cout << " this->fileIsEmpty" << this->fileIsEmpty << endl;
        if (this->fileIsEmpty) {
            cout << "文件不存在或记录为空" << endl;
            return;
        }
        this->loadRecord();
        for (map < int, vector < string > > ::iterator it = this->mRecord.begin(); it != this->mRecord.end();
        it++) {
            cout << "第" << it->first + 1 << "届:" << endl;
            cout << "冠军编号:" << it->second[0] << " 分数:" << it->second[1] << endl;
            cout << "亚军编号:" << it->second[2] << " 分数:" << it->second[3] << endl;
            cout << "季军编号:" << it->second[4] << " 分数:" << it->second[5] << endl;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

    清空记录

    • speechManager.hpp中添加清空记录函数成员声明
        // 清空记录
        void clearRecord();
    
    • 1
    • 2
    • speechManager.hpp中实现清空记录的功能,并在用户交互界面提供清空记录的功能调用
    // 清空记录
    void SpeechManager::clearRecord() {
        cout << "确认清空?" << endl;
        cout << "1.确认" << endl;
        cout << "2.返回" << endl;
        int select;
        cin >> select;
        if (select == 1) {
            // 清空文件:trunc模式,如果文件存在,则删除文件并重新创建
            ofstream ofs("speech.csv", ios::trunc);
            ofs.close();
            // 初始化属性
            this->initSpeech();
            // 创建选手
            this->createSpeaker();
            // 加载往届成绩
            this->loadRecord();
            cout << "清空成功" << endl;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    完整项目位置

  • 相关阅读:
    【数据集的制作】VOC2007数据集格式的转换(voc2yolo)与划分
    计算机网络笔记汇总链接
    中期国际2.19黄金市场分析:美国通胀数据火热,黄金面临高利率削弱的挑战
    C语言题收录(七)
    数据结构单链表
    梯度下降法总是在同一点收敛吗?
    HCIP(第十五天)
    【Ajax】如何通过axios发起Ajax请求
    词向量的维度大概多少才够?
    如何快速确定论文的选题?
  • 原文地址:https://blog.csdn.net/double_happiness/article/details/136504233