C++学习记录 六、基于STL泛化编程的演讲比赛

    科技2025-08-22  4

    再系统地过一次,夯实基础

    学习目标:

    过一遍黑马程序员C/C++学习视频

    文章目录

    学习目标:学习内容:一、C++基础入门二、通讯录管理系统三、C++核心编程三、 C++核心编程(面向对象) 继承四、基于多态的企业职工系统五、C++提高编程(1)五、C++提高编程(2)六、基于STL泛化编程的演讲比赛1. 演讲比赛程序需求1.1 比赛规则1.2 程序功能 3. 创建管理类3.1 创建文件 4. 菜单功能4.1 添加成员函数4.2 菜单功能实现4.3 测试菜单功能 5. 退出功能5.1 提供功能接口5.2 实现退出功能 6.演讲比赛功能6.1 功能分析6.2 创建选手类6.3 比赛6.3.1 成员属性添加6.3.2 初始属性6.3.3 创建选手6.3.4 开始比赛成员函数添加6.3.5 抽签6.3.6 开始比赛6.3.7 显示比赛分数6.3.8 第二轮比赛 6.4 保存分数 7.查看记录7.1 读取记录分数7.2 查看记录功能7.3 测试功能7.4 bug解决 8.清空记录8.1 清空记录功能实现8.2 测试清空 七、C++实战项目机房预约管理系统 学习产出:

    学习内容:

    一、C++基础入门


    二、通讯录管理系统


    三、C++核心编程

    三、 C++核心编程(面向对象) 继承


    四、基于多态的企业职工系统


    五、C++提高编程(1)

    五、C++提高编程(2)


    六、基于STL泛化编程的演讲比赛

    1. 演讲比赛程序需求

    1.1 比赛规则

    学校举行一场演讲比赛,共有12个人参加,比赛共两轮,第一轮为淘汰赛,第二轮为决赛。每名选手都有对应的编号,如 10001 ~ 10012比赛方式:分组比赛,每组6人第一轮分为两个小组,整体按照选手编号进行抽签后顺序演讲十个评委分别给每名选手打分,去除最高分和最低分,求的平均分为本轮选手的成绩当小组演讲完后,淘汰组内排名最后的三个选手,前三名晋级,进入下一轮的比赛第二轮为决赛,前三名胜出每轮比赛过后需要显示晋级选手的信息

    1.2 程序功能

    开始演讲比赛:完成整届比赛的流程,每个比赛阶段需要给用户一个提示,用户按任意键后继续下一个阶段查看往届记录:查看之前比赛前三名结果,每次比赛都会记录到文件中,文件用.csv后缀名保存清空比赛记录:将文件中数据清空退出比赛程序:可以退出当前程序

    3. 创建管理类

    功能描述:

    提供菜单界面与用户交互对演讲比赛流程进行控制与文件的读写交互

    3.1 创建文件

    在头文件和源文件的文件夹下分别创建speechManager.h和speechManager.cpp文件

    4. 菜单功能

    功能描述:与用户的沟通界面

    4.1 添加成员函数

    在管理类speechManager.h中添加成员函数 void show_Menu();

    //展示菜单 void show_Menu(); //speechMangaer.cpp

    4.2 菜单功能实现

    在管理类SpeechManager.cpp中实现 void SpeechManager::show_Menu() { cout << "**********************************" << endl; cout << "******** 欢迎参加演讲比赛 ********" << endl; cout << "******** 1.开始演讲比赛 ********" << endl; cout << "******** 2.查看往届记录 ********" << endl; cout << "******** 3.清空比赛记录 ********" << endl; cout << "******** 4.退出比赛程序 ********" << endl; cout << "**********************************" << endl; cout << endl; }

    4.3 测试菜单功能

    在演讲比赛流程管理系统.cpp测试菜单功能 #include<iostream> using namespace std; #include "speechManager.h" int main() { SpeechManager sm; sm.show_Menu(); system("pause"); return 0; }

    5. 退出功能

    功能描述:实现退出程序

    5.1 提供功能接口

    在main函数中提供分支选择,提供每个功能接口 #include<iostream> using namespace std; #include "speechManager.h" int main() { SpeechManager sm; int choice = 0;//用来存储用户的选项 while (true) { sm.show_Menu(); cout << "请输入您的选择" << endl; cin >> choice;//接受用户的选项 switch (choice) { case 1://开始比赛 break; case 2://查看记录 break; case 3://清空记录 break; case 0://退出系统 break; default: system("cls");//清屏 break; } } system("pause"); return 0; }

    5.2 实现退出功能

    在speechManager.h中提供退出系统的成员函数 void exitSystem(); 在speechManager.cpp中提供具体的功能实现

    void SpeechManager::exitSystem() { cout << "欢迎下次使用" << endl; system("pause"); exit(0); }

    6.演讲比赛功能

    6.1 功能分析

    比赛流程分析: 抽签 -> 开始演讲比赛 -> 显示第一轮比赛结果 -> 抽签 -> 开始演讲比赛 -> 显示前三名结果 -> 保存分数

    6.2 创建选手类

    选手类中的属性包含:选手姓名、分数头文件中创建speaker.h文件,并添加代码 #pragma once #include <iostream> using namespace std; class Speaker { public: string m_Name;//姓名 double m_Score[2];// 分数 最多有两轮得分 };

    6.3 比赛

    6.3.1 成员属性添加
    在speechManager.h中添加属性 #pragma once #include <iostream> #include <vector> #include <map> #include "speaker.h" using namespace std; //设计演讲管理类 class SpeechManager { public: //构造 和 析构 SpeechManager(); ~SpeechManager(); //展示菜单 void show_Menu(); void exitSystem(); //比赛选手 容器 12人 vector<int> v1; //第一轮晋级容器 6人 vector<int> v2; //胜利前三名容器 3人 vector<int> vVictory; //存放编号 以及对应的具体选手 容器 map<int, Speaker> m_Speaker; //记录比赛轮数 int m_Index; };
    6.3.2 初始属性
    在speechManager.h中提供开始比赛的成员函数 void initSpeech(); //初始化属性 void initSpeech(); 在speechManager.cpp中实现 void SpeechManager::initSpeech() { //容器保证为空 this->v1.clear(); this->v2.clear(); this->vVictory.clear(); this->m_Speaker.clear(); //初始化比赛论述 this->m_Index = 1; } SpeechManager构造函数中调用 void initSpeech(); SpeechManager::SpeechManager() { //初始化属性 this->initSpeech(); }
    6.3.3 创建选手
    在speechManager.h中提供开始比赛的成员函数 createSpeaker(); //初始化创建12名选手 void createrSpeaker(); 在speechManager.cpp中实现 void SpeechManager::createrSpeaker() { string nameSeed = "ABCDEFGHIJKL"; for (int i = 0; i < nameSeed.size(); i++) { string name = "选手"; name += nameSeed[i]; Speaker sp; sp.m_Name = name; for (int i = 0; i < 2; i++) { sp.m_Score[i] = 0; } //12名选手编号 this->v1.push_back(i + 10001); //选手编号 以及对应的选手 存放到map容器中 this->m_Speaker.insert(make_pair(i + 10001, sp)); } } speechManager类的构造函数中调用 SpeechManager::SpeechManager() { //初始化属性 this->initSpeech(); //创建选手 this->createrSpeaker(); } //测试代码 for (map<int, Speaker>::iterator it = sm.m_Speaker.begin(); it != sm.m_Speaker.end(); it++) { cout << "选手编号:" << it->first << " 姓名:" << it->second.m_Name << " 成绩:" << it->second.m_Score[0] << endl; }

    6.3.4 开始比赛成员函数添加
    在speechManager.h中提供开始比赛的成员函数 void startSpeech();该函数功能是主要控制比赛的流程 //开始比赛 - 比赛流程控制 void startSpeech(); 在speechManager.cpp中实现可以将整个比赛的流程写到函数中 void SpeechManager::startSpeech() { //第一轮比赛 //1、抽签 //2、比赛 //3、显示晋级结果 //第二轮比赛 //1、抽签 //2、比赛 //3、显示最终结果 //4、保存分数 }
    6.3.5 抽签

    功能描述:

    正式比赛前,所有选手的比赛顺序需要打乱,我们只需要将存放选手编号的容器 打乱次序即可在speechManager.h中提供抽签的成员函数 void speechDraw(); //抽签 void speechDraw(); 在speechManager.cpp中实现 void SpeechManager::speechDraw() { cout << "第" << this->m_Index << " >> 轮比赛选手正在抽签" << endl; cout << "-----------------------------------------" << endl; cout << "抽签后演讲顺序如下:" << endl; if (this->m_Index == 1) { random_shuffle(v1.begin(), v1.end()); for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++) { cout << *it << " "; } cout << endl; } else { random_shuffle(v2.begin(), v2.end()); for (vector<int>::iterator it = v2.begin(); it != v2.end(); it++) { cout << *it << " "; } cout << endl; } cout << "-----------------------------------------" << endl; system("pause"); cout << endl; } 在startSpeech比赛流程控制的函数中,调用抽签函数 void SpeechManager::startSpeech() { //第一轮比赛 //1、抽签 this->speechDraw(); //2、比赛 //3、显示晋级结果 //第二轮比赛 //1、抽签 //2、比赛 //3、显示最终结果 //4、保存分数 } 在main函数中,分支1选项中,调用开始比赛的接口 case 1://开始比赛 sm.startSpeech(); break;
    6.3.6 开始比赛
    在speechManager.h中提供比赛的成员函数 void speechContest(); //比赛 void speechContest(); 在speechManager.cpp中实现 void SpeechManager::speechContest() { cout << "------------- 第 << " << this->m_Index << " >> 轮比赛正式开始 -------------" << endl; //greater<double>会按照降序自动排序 multimap<double, int, greater<double>> groupScore;//临时容器,保存key分数 value选手编号 int num = 0;//记录人员数,6个为一组 vector<int> v_Src;//比赛的人员容器 if (this->m_Index == 1) { v_Src = v1; } else { v_Src = v2; } //遍历所有的参赛选手 for (vector<int>::iterator it = v_Src.begin(); it != v_Src.end(); it++) { num++; //评委打分 deque<double> d; for (int i = 0; i < 10; i++) { double score = (rand() % 401 + 600) / 10.f;//600 ~ 1000 //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();//获取平均分 //每个人平均分 this->m_Speaker[*it].m_Score[this->m_Index - 1] = avg; //6个人一组,用临时容器保存 groupScore.insert(make_pair(avg, *it)); 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->m_Speaker[it->second].m_Name << " 成绩:" << this->m_Speaker[it->second].m_Score[this->m_Index - 1] << endl; } int count = 0; //取前三名 for (multimap<double, int, greater<double>>::iterator it = groupScore.begin(); it != groupScore.end() && count < 3; it++, count++) { if (this->m_Index == 1) { v2.push_back((*it).second); } else { vVictory.push_back((*it).second); } } groupScore.clear(); cout << endl; } } cout << "------------- 第 << " << this->m_Index << " >> 轮比赛完毕 -------------" << endl; system("pause"); } 在startSpeech比赛流程控制的函数中,调用比赛函数 void SpeechManager::startSpeech() { //第一轮比赛 //1、抽签 this->speechDraw(); //2、比赛 this->speechContest(); //3、显示晋级结果 //第二轮比赛 //1、抽签 //2、比赛 //3、显示最终结果 //4、保存分数 }
    6.3.7 显示比赛分数
    在speechManager.h中提供显示分数的成员函数 void showScore(); //显示比赛结果 void showScore(); 在speechManager.cpp中实现 void SpeechManager::showScore() { cout << "------------- 第 << " << this->m_Index << " >> 轮晋级选手信息如下: -------------" << endl; vector<int> v; if (this->m_Index == 1) { v = v2; } else { v = vVictory; } for (vector<int>::iterator it = v.begin(); it != v.end(); it++) { cout << "选手编号:" << *it << " 姓名:" << m_Speaker[*it].m_Name << " 得分:" << m_Speaker[*it].m_Score[this->m_Index - 1] << endl; } cout << endl; system("pause"); system("cls"); this->show_Menu(); } 在startSpeech比赛流程控制的函数中,调用显示得分函数 void SpeechManager::startSpeech() { //第一轮比赛 //1、抽签 this->speechDraw(); //2、比赛 this->speechContest(); //3、显示晋级结果 this->showScore(); //第二轮比赛 //1、抽签 //2、比赛 //3、显示最终结果 //4、保存分数 }
    6.3.8 第二轮比赛

    第二轮比赛流程同第一轮,只是比赛的轮是+1,其余流程不变

    在startSpeech比赛流程控制的函数中,加入第二轮的流程 void SpeechManager::startSpeech() { //第一轮比赛 //1、抽签 this->speechDraw(); //2、比赛 this->speechContest(); //3、显示晋级结果 this->showScore(); //第二轮比赛 this->m_Index++; //1、抽签 this->speechDraw(); //2、比赛 this->speechContest(); //3、显示最终结果 this->showScore(); //4、保存分数 }

    6.4 保存分数

    功能描述:

    将每次演讲比赛的得分记录在文件中

    功能实现:

    在speechManager.h中田间保存记录的成员函数 void saveRecord(); //保存记录 void saveRecord(); 在speechManager.cpp中实现 void SpeechManager::saveRecord() { ofstream ofs; ofs.open("seepch.csv", ios::out | ios::app);//用输出的方式打开文件 --写文件 //将每个人数据写入到文件中 for (vector<int>::iterator it = vVictory.begin(); it != vVictory.end(); it++) { ofs << *it << "," << m_Speaker[*it].m_Score[1] << ","; } ofs << endl; //关闭文件 ofs.close(); cout << "记录已保存" << endl; } 在startSpeech比赛流程控制的函数中,最后调用保存记录分数函数 void SpeechManager::startSpeech() { //第一轮比赛 //1、抽签 this->speechDraw(); //2、比赛 this->speechContest(); //3、显示晋级结果 this->showScore(); //第二轮比赛 this->m_Index++; //1、抽签 this->speechDraw(); //2、比赛 this->speechContest(); //3、显示最终结果 this->showScore(); //4、保存分数 this->saveRecord(); }

    7.查看记录

    7.1 读取记录分数

    在speechManager.h中添加加载记录的成员函数 void loadRecord();添加判断文件是否为空的标志 bool fileIsEmpty;添加往届记录的容器 map<int, vector<string>> m_Record;

    其中m_Record中的key代表第几届,value记录具体信息

    //读取记录 void loadRecord(); //文件为空的标志 bool fileIsEmpty; //往届记录 map<int, vector<string>> m_Record; 在speechManager.cpp中实现 void SpeechManager::loadRecord() { ifstream ifs("speech.csv", ios::in);//输入流对象 读取文件 if ( !ifs.is_open() ) { this->fileIsEmpty = true; cout << "文件不存在" << endl; ifs.close(); return; } char ch; ifs >> ch; if (ifs.eof()) { cout << "文件为空!" << endl; ifs.close(); return; } //文件不为空 this->fileIsEmpty = false; ifs.putback(ch);//读取的单个字符放回去 string data; int index = 0; while (ifs >> data) { vector<string> v; int pos = -1; int start = 0; while (true) { pos = data.find(",", start);//从0开始查找 逗号 if (pos == -1) { break;//找不到 break返回 } //找到逗号,进行分割 参数1 起始位置, 参数2 截取长度 string temp = data.substr(start, pos - start); v.push_back(temp); start = pos + 1; } this->m_Record.insert(make_pair(index, v)); index++; } ifs.close(); } 在SpeechManager构造函数中调用获取往届记录函数 SpeechManager::SpeechManager() { //初始化属性 this->initSpeech(); //创建选手 this->createrSpeaker(); //获取记录 this->loadRecord(); }

    7.2 查看记录功能

    在speechManager.h中添加显示保存记录的成员函数 void showRecord(); //显示往届得分 void showRecord(); 在speechManager.cpp中实现 void SpeechManager::showRecord() { for (int i = 0; i < this->m_Record.size(); i++) { cout << "第" << i + 1 << "届" << "冠军编号:" << this->m_Record[i][0] << " 得分:" << this->m_Record[i][1] << " " "亚军编号:" << this->m_Record[i][2] << " 得分:" << this->m_Record[i][3] << " " "季军编号:" << this->m_Record[i][4] << " 得分:" << this->m_Record[i][5] << endl; } system("pause"); system("cls"); } 在startSpeech比赛流程控制的函数中,最后调用显示函数 case 2://查看记录 sm.showRecord(); break;

    7.3 测试功能

    目前程序中有基础Bug:

    查看往届记录,若文件不存在或为空,并未提示

    解决方式:在showRecord函数中,开始判断文件状态并加以判断

    void SpeechManager::showRecord() { if (this->fileIsEmpty) { cout << "文件不存在,或记录为空!" << endl; } for (int i = 0; i < this->m_Record.size(); i++) { cout << "第" << i + 1 << "届 " << "冠军编号:" << this->m_Record[i][0] << " 得分:" << this->m_Record[i][1] << " " "亚军编号:" << this->m_Record[i][2] << " 得分:" << this->m_Record[i][3] << " " "季军编号:" << this->m_Record[i][4] << " 得分:" << this->m_Record[i][5] << endl; } system("pause"); system("cls"); } 若记录为空或不存在,比完赛后依然提示记录为空

    解决方式:saveRecord中更新文件为空的标志

    void SpeechManager::saveRecord() { ofstream ofs; ofs.open("seepch.csv", ios::out | ios::app);//用输出的方式打开文件 --写文件 //将每个人数据写入到文件中 for (vector<int>::iterator it = vVictory.begin(); it != vVictory.end(); it++) { ofs << *it << "," << m_Speaker[*it].m_Score[1] << ","; } ofs << endl; //关闭文件 ofs.close(); cout << "记录已保存" << endl; //有记录了,文件不为空 this->fileIsEmpty = false; } 比赛完后查不到本届比赛的记录,没有实时更新

    解决方式:比赛完毕后,所有数据重置

    void SpeechManager::startSpeech() { //第一轮比赛 //1、抽签 this->speechDraw(); //2、比赛 this->speechContest(); //3、显示晋级结果 this->showScore(); //第二轮比赛 this->m_Index++; //1、抽签 this->speechDraw(); //2、比赛 this->speechContest(); //3、显示最终结果 this->showScore(); //4、保存分数 this->saveRecord(); //重置比赛 //初始化属性 this->initSpeech(); //创建选手 this->createrSpeaker(); //获取往届记录 this->loadRecord(); cout << "本届比赛完毕!" << endl; system("pause"); system("cls"); } 在初始化时,没有初始化记录容器

    解决方式:initSpeech中添加初始化记录容器

    void SpeechManager::initSpeech() { //容器保证为空 this->v1.clear(); this->v2.clear(); this->vVictory.clear(); this->m_Speaker.clear(); //初始化比赛轮数 this->m_Index = 1; //初始化记录容器 this->m_Record.clear(); }

    7.4 bug解决

    8.清空记录

    8.1 清空记录功能实现

    在speechManager.h中添加清空记录的成员函数 void clearRecord(); //清空记录 void clearRecord(); 在speechManager.cpp中实现 void SpeechManager::clearRecord() { cout << "确认清空?" << endl; cout << "1、确认" << endl; cout << "2、返回" << endl; int select = 0; cin >> select; if (select == 1) { //打开模式 ios::trunc 如果存在删除文件并重新创建 ofstream ofs("seepch.csv", ios::trunc); ofs.close(); //初始化属性 this->initSpeech(); //创建选手 this->createrSpeaker(); //获取往届记录 this->loadRecord(); cout << "清空成功" << endl; } system("pause"); system("cls"); }

    8.2 测试清空

    在main函数分支3选项中,调用清空比赛记录的接口

    case 3://清空记录 sm.clearRecord(); break;

    七、C++实战项目机房预约管理系统


    学习产出:

    1、github 啃STL简化项目,能够自己实现STL相关项目 2、做一个微信小程序,具体功能暂定

    Processed: 0.040, SQL: 8