学习视频链接
演讲比赛程序需求 比赛规则 学校句型一场演讲比赛,共有12个人参加,比赛共两轮,第一轮为淘汰赛,第二轮为决赛
每名选手都有对应的编号,如10001~10012
比赛方式:分组比赛,每组6个人
第一轮分为两个小组,整体按照选手编号进行抽签后顺序演讲
十个评委分别给每名选手打分,去除最高分和最低分,求的平均分为本轮选手的成绩
第二轮为决赛,前三名胜出
每轮比赛过后需要显示晋级选手的信息
程序功能
创建管理类 功能描述:
提供菜单界面与用户交互
对演讲比赛流程进行控制
与文件的读写交互
创建文件 在头文件和源文件的文件夹下分别创建speechManager.h和speechManager.cpp文件
菜单功能 功能描述:与用户的沟通界面
添加成员函数 在管理类speechManager.h中添加成员函数 void show_Menu();
void SpeechManager::show_Menu () { cout << "********************" << endl; cout << "**欢迎参加演讲比赛**" << endl; cout << "1.开始演讲比赛" << endl; cout << "2.查看往届记录" << endl; cout << "3.清空比赛记录" << endl; cout << "0.退出比赛程序" << endl; cout << "********************" << endl; cout << endl; }
测试菜单功能:
退出功能 功能描述:实现退出程序
提供功能接口 在main函数中提供分支选择,提供每个功能接口
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 : sm.exitSystem (); break ; default : system ("cls" ); break ; } } system ("pause" ); return 0 ; }
实现退出功能 在speechManager.h中提供退出系统的成员函数void exitSystem();
在speechManager.cpp中提供具体的功能实现
void SpeechManager::exitSystem () { cout << "欢迎下次使用" << endl; system ("pause" ); exit (0 ); }
演讲比赛功能 功能分析 比赛流程分析:
抽签-》开始演讲比赛-》显示第一轮比赛结果-》
抽签-》开始演讲比赛-》显示前三名结果-》保存分数
创建选手类 选手类中的属性包含:选手姓名、分数
头文件中创建speaker.h文件,并添加代码
#pragma once #include <iostream> using namespace std;class Speaker { public : string m_Name; double m_Score[2 ]; };
比赛 成员属性添加 在speechManager.h中添加属性
vector<int >v1; vector<int >v2; vector<int >vVictory; map<int , Speaker>m_Speaker; int m_Index;
初始化属性 在speechManager.h中提供开始比赛的成员函数void initSpeech();
在speechManager.cpp中实现void initSpeech();
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 (); }
创建选手 在speechManager.h中提供开始比赛的成员函数void createSpeaker();
在speechManager.cpp中实现void createSpeaker();
void SpeechManager::createSpeaker () { string nameSeed = "ABCDEFGHIJL" ; for (int i = 0 ; i < nameSeed.size (); i++) { string name = "选手" ; name = name + nameSeed[i]; Speaker sp; sp.m_Name = name; for (int j = 0 ; j < 2 ; j++) { sp.m_Score[j] = 0 ; } this ->v1.push_back (i + 10001 ); this ->m_Speaker.insert ( make_pair (i + 10001 , sp)); } }
SpeechManager类的构造函数中调用void createSpeaker();
SpeechManager::SpeechManager () { this ->initSpeech (); this ->createSpeaker (); }
测试在main函数中,可以在创建完管理对象后,使用下列代码测试12名选手初始状态
SpeechManager sm; 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; }
开始比赛成员函数 在speechManager.h中提供开始比赛的成员函数void startSpeech();
该函数功能是控制比赛的流程
在speechManager.cpp中将startSpeech的空实现先写入
先将整个比赛的流程写到函数中
void SpeechManager::startSpeech () { }
抽签 功能描述:
正式比赛前,所有选手的比赛顺序需要打乱,我们只需要将存放选手编号的容器打乱次序
在speechManager.h中提供抽签的成员函数void speechDraw();
在speechManager.cpp中实现void speechDraw();
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比赛流程控制函数中,调用抽签函数
在main函数中,分支1选项中,调用开始比赛的接口
测试结果:
开始比赛 在speechManager.h中提供比赛的成员函数void speechContest();
在speechManager.cpp中实现成员函数void speechContest();
补充知识
在C++中,deque(双端队列)是一种序列容器,类似于 vector,但是它允许在头尾两端进行高效的插入和删除操作。deque 是 “double-ended queue” 的缩写。
deque 和 vector 的主要区别在于,在 deque 中,元素存储在一个或多个连续的块中,而不是单个连续的数组。这使得 deque 更适合在头尾进行插入和删除操作,因为这些操作在 deque 中的开销更小。
deque 的特点包括:
高效的插入和删除操作:在 deque 的头尾插入和删除元素的时间复杂度为 O(1)。
随机访问:deque 支持通过索引随机访问元素,时间复杂度为 O(1)。
动态增长:deque 可以动态地增长以容纳更多的元素,因此没有固定的大小限制。
内存分配:deque 使用多个块来存储元素,这意味着在插入和删除操作时,不需要移动所有元素,因此在特定情况下可能比 vector 更高效。
在使用 deque 时,你可以像使用数组一样通过索引访问元素,也可以使用 push_front()
、push_back()
、pop_front()
、pop_back()
等成员函数在头尾插入和删除元素。
使用 deque 需要包含头文件 <deque>
。deque 是标准模板库(STL)中的一部分,因此可以在标准 C++ 中直接使用。
void SpeechManager::speechContest () { cout << "---------------第" << this ->m_Index << "轮比赛正式开始-----------" << endl; multimap<double , int , greater<double >> groupSorce; int num = 0 ; 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 ; d.push_back (score); } sort (d.begin (), d.end (), greater <double >()); d.pop_back (); d.pop_front (); 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; groupSorce.insert (make_pair (avg, *it)); if (num % 6 == 0 ) { cout << "第" << num / 6 << "小组比赛名次:" << endl; for (multimap<double , int , greater<double >>::iterator it = groupSorce.begin (); it != groupSorce.end (); it++) { cout << "编号:" << it->second << "\t姓名:" << this ->m_Speaker[it->second].m_Name << "\t成绩:" << this ->m_Speaker[it->second].m_Score[this ->m_Index - 1 ] << endl; } int count = 0 ; for (multimap<double , int , greater<double >>::iterator it = groupSorce.begin (); it != groupSorce.end () && count < 3 ; it++, count++) { if (this ->m_Index == 1 ) { v2.push_back ((*it).second); } else { vVictory.push_back ((*it).second); } } groupSorce.clear (); cout << endl; } } cout << "---------------第" << this ->m_Index << "轮比赛完毕--------------" << endl; system ("pause" ); }
测试:
显示比赛分数 在speechManager.h中提供比赛的成员函数 void showScore();
在speechManager.cpp中实现成员函数 void showScore();
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 << "\t姓名:" << this ->m_Speaker[*it].m_Name << "\t得分:" << this ->m_Speaker[*it].m_Score[this ->m_Index - 1 ] << endl; } cout << endl; system ("pause" ); system ("cls" ); this ->show_Menu (); }
第二轮比赛 第二轮比赛流程同第一轮,知识比赛轮数+1,其余流程不变
在startSpeech比赛流程控制函数中,加入第二轮的流程
void SpeechManager::startSpeech () { this ->speechDraw (); this ->speechContest (); this ->showScore (); this ->m_Index++; this ->speechDraw (); this ->speechContest (); this ->showScore (); }
测试:
保存分数 功能描述:
将每次演讲比赛的得分记录到文件中
功能实现:
在speechManager.h中添加保存记录的成员函数 void saveRecord();
在speechManager.cpp中实现成员函数 void saveRecord();
void SpeechManager::saveRecord () { ofstream ofs; ofs.open ("speech.csv" , ios::out | ios::app); for (vector<int >::iterator it = vVictory.begin (); it != vVictory.end (); it++) { ofs << *it << "," << this ->m_Speaker[*it].m_Score[1 ] << "," ; } ofs << endl; ofs.close (); cout << "记录已经保存" << endl; this ->fileIsEmpty = false ; }
在startSpeech比赛流程控制函数中,最后调用保存记录分数函数
void SpeechManager::startSpeech () { this ->speechDraw (); this ->speechContest (); this ->showScore (); this ->m_Index++; this ->speechDraw (); this ->speechContest (); this ->showScore (); this ->saveRecord (); this ->initSpeech (); this ->createSpeaker (); this ->loadRecord (); cout << "---------------本届比赛完毕----------------" << endl; system ("pause" ); system ("cls" ); }
查看记录 读取记录分数 在speechManager.h中添加保存记录的成员函数 void loadRecord();
添加判断文件是否为空的标志 bool fileIsEmpty;
添加往届记录的容器 map<int, vector<string>> m_Record;
其中m_Record中的key代表第几届,value记录具体的信息
bool fileIsEmpty;map<int , vector<string>>m_Record;
void SpeechManager::initSpeech () { this ->v1.clear (); this ->v2.clear (); this ->vVictory.clear (); this ->m_Speaker.clear (); this ->m_Index = 1 ; this ->m_Record.clear (); }
在speechManager.cpp中实现成员函数 void loadRecord();
void SpeechManager::loadRecord () { ifstream ifs ("speech.csv" , ios::in) ; int index = 0 ; if (!ifs.is_open ()) { this ->fileIsEmpty = true ; ifs.close (); return ; } if (ifs.eof ()) { this ->fileIsEmpty = true ; ifs.close (); return ; } this ->fileIsEmpty = false ; string data; while (ifs >> data) { vector<string>v; int pos = -1 ; int start = 0 ; while (true ) { pos = data.find ("," , start); if (pos == -1 ) { break ; } 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.h中添加保存记录的成员函数 void showRecord();
在speechManager.cpp中实现成员函数 void showRecord();
void SpeechManager::showRecord () { if (this ->fileIsEmpty) { cout << "文件不存在,或记录为空!" << endl; } else { for (int i = 0 ; i < this ->m_Record.size (); i++) { cout << "第" << i + 1 << "届\t" << "冠军编号:" << this ->m_Record[i][0 ] << "\t得分:" << this ->m_Record[i][1 ] << "\t" "亚军编号:" << this ->m_Record[i][2 ] << "\t得分:" << this ->m_Record[i][3 ] << "\t" "季军编号:" << this ->m_Record[i][4 ] << "\t得分:" << this ->m_Record[i][5 ] << endl; } } system ("pause" ); system ("cls" ); }
清空记录 在speechManager.h中添加清空记录的成员函数 void clearRecord();
在speechManager.cpp中实现成员函数 void clearRecord();
void SpeechManager::clearRecrd () { cout << "确认清空?" << endl; cout << "1.确认" << endl; cout << "2.返回" << endl; int select = 0 ; cin >> select; if (select == 1 ) { ofstream ofs ("speech.csv" , ios::trunc) ; ofs.close (); this ->initSpeech (); this ->createSpeaker (); this ->loadRecord (); cout << "清空成功!" << endl; } system ("pause" ); system ("cls" ); }
主函数 #include <iostream> using namespace std;#include "speechManager.h" #include <ctime> int main () { srand ((unsigned int )time (NULL )); SpeechManager sm; int choice = 0 ; while (true ) { sm.show_Menu (); cout << "请输入您的选择:" << endl; cin >> choice; switch (choice) { case 1 : sm.startSpeech (); break ; case 2 : sm.showRecord (); break ; case 3 : sm.clearRecrd (); break ; case 0 : sm.exitSystem (); break ; default : system ("cls" ); break ; } } system ("pause" ); return 0 ; }