1、开始演讲比赛
1.1 成员属性添加和初始化
根据上一篇的功能分析,需要在speechManager.h中添加成员属性和初始化成员属性的函数声明,并在speechManager.cpp中写初始化成员属性函数的实现。
实现步骤:
- 首先,第一轮比赛有12个人,对应12个编号;第二比赛有6人,对应6个编号;最后有3个人胜出,因此可以准备3个vector容器存放这三组编号。也可以不用3个vector容器,用1个大容器来嵌套3个小容器,通过大容器来访问到3个小容器,可以后续改进
- 其次,仅存编号是不够的,在显示选手信息时至少需要展示选手的姓名和得分,而且选手编号和选手的信息是对应的。因此可以使用map容器(对组)来存放选手编号和选手的信息(姓名和得分),由于选手编号不可能重复,采用map容器即可,不需要multimap容器。
- 然后,还需要需要一个参数来记录比赛的当前轮数。
以上3步这些都需要添加在流程管理系统中,属于speechManager的成员属性
- 接着,还需要初始化以上成员属性,也就是把容器都置空和比赛轮数置1。初始化时,在speechManager.h头文件写初始化的函数声明,
void initSpeech();
;在peechManager.cpp源文件中写函数的实现。 - 最后,在构造函数中调用初始化成员属性函数
代码展示:
- 在speechManager.h中添加成员属性和初始化成员属性函数
initSpeech()
//初始化成员属性
void initSpeech();
//成员属性
vector<int> v1;//第一轮比赛的12名选手编号
vector<int> v2;//第二轮比赛的6名选手编号 第一轮晋级6名选手编号
vector<int> vfinal;//最后胜出的3名选手编号
map<int, Speaker> m_Speaker;//存放选手编号和选手信息(姓名 分数)
int m_index;//记录当前比赛轮数
- 在speechManager.cpp中实现
void initSpeech();
//构造函数
SpeechManager::SpeechManager()
{
this->initSpeech();
}
//初始化成员属性
void SpeechManager::initSpeech()
{
//容器置空 比赛轮数置1
this->v1.clear();
this->v2.clear();
this->vfinal.clear();
//初始化比赛轮数
this->m_index = 1;
}
1.2 选手的具体创建
上面做好了选手类创建以及选手信息的存放等准备工作之后,接下来实现12名选手的具体创建,包括选手编号、姓名、初试分数等。
实现步骤:
- 在speechManager.h中提供开始比赛的的成员函数
void createSpeaker();
- 在speechManager.cpp中实现
void createSpeaker();
- SpeechManager类的构造函数中调用
void createSpeaker();
- 基于SCL的演讲比赛流程管理系统.cpp中的main函数中测试,在创建完管理对象后,测试12名选手是否初始化
代码展示:
- speechManager.cpp
//构造函数
SpeechManager::SpeechManager()
{
this->initSpeech(); //成员属性初始化
this->createSpeaker(); //创建12名选手
}
//12名选手的具体创建
void SpeechManager::createSpeaker()
{
string nameseed = "ABCDEFGHIJKL";
for (int i = 0; i < nameseed.size(); i++)
{
string name = "选手";
name += nameseed[i];
//具体选手信息初始化
Speaker speaker;
speaker.m_Name = name;
for (int j = 0; j < 2; j++)
{
speaker.m_Score[j] = 0;
}
//创建选手编号 v1存放第一轮选手信息
this->v1.push_back(10001 + i);
this->m_Speaker.insert(make_pair(10001 + i, speaker));
}
}
注意:
在初始化选手的分数时,注意m_Score是一个数组,用for循环实现每个选手的两个分数都置0;
speaker是一个map容器,选手编号和选手信息放入这个map中,key是编号10001+i,value是具体选手
- 基于SCL的演讲比赛流程管理系统.cpp
SpeechManager sm;
//测试12名选手的初始化状态
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;
}
测试效果:
1.3 开始比赛流程 成员函数添加
实现步骤:
- 在speechManager.h中提供开始比赛的的成员函数
void startSpeech();
- 该函数功能是主要控制比赛的流程
- 在speechManager.cpp中将startSpeech的先写入空实现,可以先将整个比赛的流程伪代码写到函数中,把框架搭起来
//开始比赛 整个比赛流程的控制函数
void SpeechManager::startSpeech()
{
//第一轮比赛开始
//抽签
//比赛
//显示晋级结果
//第二轮比赛开始
//抽签
//比赛
//显示晋级结果
//保存分数
}
1.3 选手抽签 成员函数添加
实现步骤:
- 在speechManager.h中提供开始比赛的的成员函数
void speechDraw();
- 该函数功能是实现两轮比赛前选手的比赛顺序抽签
- 在speechManager.cpp中写speechDraw的具体实现
- 抽签就是对选手的编号顺序打乱,主要用到random_shuffle这个函数,然后再分组显示选手的抽签顺序
- 要对两轮比赛的选手都进行抽签,用m_index 这个成员属性来控制第几轮比赛的选手进行抽签
- 最后在
void SpeechManager::startSpeech()
中加上选手抽签成员函数,并在基于SCL的演讲比赛流程管理系统.cpp中测试
代码展示:
//开始比赛 整个比赛流程的控制函数
void SpeechManager::startSpeech()
{
//第一轮比赛开始
//抽签
this->speechDraw();
//比赛
//显示晋级结果
//第二轮比赛开始
//抽签
//比赛
//显示晋级结果
//保存分数
}
//抽签
void SpeechManager::speechDraw()
{
//第一轮
cout << "第 " << this->m_index << " 轮比赛选手正在抽签" << endl;
cout << string(40, '-') << endl;
cout << "抽签后第一轮演讲比赛选手比赛顺序:" << endl;
if (this->m_index == 1)
{
//第一轮比赛抽签 v1容器元素打乱 第一轮选手编号
random_shuffle(v1.begin(), v1.end());
//显示打乱之后的选手编号
int n = int(v1.size()) + 1;
int half = n / 2;
cout << "第一组选手:" << endl;
vector<int>::iterator it = v1.begin();
while (n-- && n > half)
{
cout << *it << " ";
it++;
}
cout << endl << endl;
cout << "第二组选手:" << endl;
while (n--)
{
cout << *it << " ";
it++;
}
cout << endl << string(40, '-') << endl;
system("pause");
cout << endl;
}
else
{
//第二轮比赛抽签 v2容器元素打乱 第二轮选手编号
random_shuffle(v2.begin(), v2.end());
//显示打乱之后的选手编号 这6人直接一组
for (vector<int>::iterator it = v2.begin(); it != v2.end(); it++)
{
cout << *it << " ";
}
cout << endl;
cout << endl << string(40, '-') << endl;
system("pause");
cout << endl;
}
}
1.4 开始比赛 成员函数添加
实现步骤:
- 在speechManager.h中提供比赛的的成员函数
void speechContest();
- 在speechManager.cpp中实现成员函数
void speechContest();
- 最后在
void SpeechManager::startSpeech()
中加上开始比赛成员函数,并在基于SCL的演讲比赛流程管理系统.cpp中测试
代码展示:
void SpeechManager::speechContest()
{
cout << string(15, '*') << " 第 " << this->m_index << " 轮比赛正式开始 " << string(15, '*') << endl;
int num = 0;//统计当前参赛选手的个数
//保存当前组选手成绩 后续打印和决出晋级和胜出选手 key是成绩 value是编号 key降序排序
multimap<double, int, greater<double>> mspeech;
//第几轮比赛 获取当前参赛选手 选手容器获取v1还是v2
vector<int>vspeech;
if (this->m_index == 1)
{
vspeech = v1;
}
else
{
vspeech = v2;
}
//比赛 给每个选手打分
for (vector<int>::iterator it = vspeech.begin(); it != vspeech.end(); it++)
{
num++;
deque<double> d;//存10个分数
for (int i = 0; i < 10; i++)
{
double score = (rand() % 401 + 600) / 10.f;//打分 随机出小数
//cout << score << " ";//测试
d.push_back(score);//1个选手的所有得分
}
//cout << endl;//测试 换行
//计算当前选手的总分和平均分 成绩降序排序 谓词头文件#include<functional>
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();
平均分测试
//cout << "编号:" << *it << " 姓名:" << this->m_Speaker[*it].m_Name << " 成绩:" << avg << endl;
//存当前选手的成绩 *it选手编号 this->m_index-1 第一轮比赛就是第一个分数 第二轮比赛就是第二个分数
this->m_Speaker[*it].m_Score[this->m_index-1] = avg;
//保存当前选手分数 key是成绩 value是编号 key降序排序
mspeech.insert(make_pair(avg, *it));
//每一组输出 分数打印 选出晋级选手和胜出选手
if (num % 6 == 0)//每6名选手一组 输出一组选手成绩 num / 6 表示第几组
{
cout << "第 " << num / 6 << " 组选手" << endl;
for (multimap<double, int, greater<double>>::iterator it = mspeech.begin(); it != mspeech.end(); it++)
{
cout << "编号:" << it->second << " 姓名:" << this->m_Speaker[it->second].m_Name << " 成绩:" << it->first << endl;
}
//前三名
int count = 0;
for (multimap<double, int, greater<double>>::iterator it = mspeech.begin(); it != mspeech.end() && count < 3; it++, count++)
{
if (this->m_index == 1)//第一轮比赛
{
v2.push_back(it->second);//晋级选手
}
else//第二轮比赛
{
vfinal.push_back(it->second);
}
}
mspeech.clear();//一组处理完清空 否则下一组处理时会保留上一组信息
}
}
//比赛结束
cout << string(15, '*') << " 第 " << this->m_index << " 轮比赛结束 " << string(15, '*') << endl;
cout << endl;
system("pause");
cout << endl;
}
//开始比赛 整个比赛流程的控制函数
void SpeechManager::startSpeech()
{
//第一轮比赛开始
//抽签
this->speechDraw();
//比赛
this->speechContest();
//显示晋级结果
//第二轮比赛开始
this->m_index++;
//抽签
speechDraw();
//比赛
this->speechContest();
//显示晋级结果
//保存分数
}
说明: 代码主要分为两大块:一个是计算最终成绩,另一个是成绩的保存和打印,以及选出晋级选手
一、计算最终成绩
- 首先提示用户,当前是第几轮比赛,并且用m_index来控制比赛轮数。要注意的是,m_index初始化为1,也就是第一轮比赛。第一轮比赛结束之后,m_index要+1,表示进行第二轮比赛。
- 由于抽签在比赛之前完成了,接下来只要取出抽签后的选手参加即可。由于两轮比赛的选手不一样,通过m_index来筛选当前参赛选手,并且用一个vector临时容器v_sc来存放当前的参赛选手编号,具体如下:
- 当第一轮比赛,m_index=1时,取出v1容器中第一轮参赛选手编号,并v_sc=v1;
- 当第二轮比赛,m_index=2时,取出v2容器中第二轮参赛选手编号,并v_sc=v2。
- 开始比赛后,通过遍历v_sc,逐个给每个选手打分,每个选手都有10个分数。为了计算最终成绩,每个选手的所有成绩都要临时保存,采用deque来存放选手的10个评委分,具体如下:
- 遍历v_sc后,创建deque容器,通过for循环、rand()产生10个随机分数并逐个通过push_back存入deque,这里可以把分数输出做一个测试;
- 要注意的是,为了产生带小数的分数,可以生成600-1000的整数再除以10,此时分数是double浮点型的;
- 这里用deque容器是为了在降序排序后,方便两头操作,去掉最高最低分。
- 为了在显示成绩时直观地看到前三名,对分数进行降序排序,使用sort算法和谓词greater,然后通过pop_front和pop_back去掉最高、最低分。
- 使用accumalate计算当前选手的分数总和,计算平均分得到选手的最终成绩。要注意的是,计算平均分统计的是deque的元素个数,并为了保证数据统一,把deque.size()强转为double类型。
二、成绩的保存和打印,以及选出晋级选手
- 每个选手的最终成绩计算出来之后,需要保存在map容器m_Speaker中,key是int,编号;value是Speaker。
- 分组打印成绩,并且取出每组的前三名选手晋级,比赛轮数不同有不一样的处理,具体如下:
- 第一轮比赛有两组,每组六人。按照成绩降序打印第一组的成绩。另外,取出前三名的选手晋级第二轮比赛,选手编号存放在v2容器中,第二组选手也做同样的处理;
- 第二轮比赛只有六人,直接按照成绩降序打印成绩,并且取出前三名做为获胜选手,选手编号存放在vfinal容器中。
- 每组只有6人,因此可以通过v_sc的元素个数对6取模来控制成绩的打印、选手的晋级和胜出。
- 在遍历选手前定义一个统计数num,每遍历一个选手就num+1。这样可以保证第一轮比赛时,num是12,第一次对6取模就是第一组,第二次取模就是第二组;第二轮时,num是6。
- 打印分数时,当比赛是第一轮时,每一组(每组六名选手)的成绩都要显示,并且按照成绩降序显示成绩和信息。
- 由于是分组打印选手成绩,因此用multimap容器m_sc来存放当前组的选手成绩和编号,key对应选手的成绩,value对应选手的编号。
- 用multimap而不用map,是因为每个选手的编号和成绩是一组数据,而且有可能选手的成绩会相同,注意multimap的key值可以重复。
- 为了后期成绩打印方便,成绩使用降序排序,m_sc加入谓词,谓词类型的double。
- 保存分数时,通过选手编号索引到选手成绩的存放位置。注意,无论是第一轮还是第二轮比赛,m_sc都只有六祖成绩和编号。分别是第一轮第一组,六祖成绩;第一轮第二组,六祖成绩;第二轮比赛,六组成绩和编号。
- 要注意的是,第一组取走前三名选手编号,通过一个统计数count来控制,获取前三名选手的编号。
- 一组选手比赛完之后,m_sc要清空,存放下一组选手的成绩,否则还会保留上一组的选手成绩。
- 最后提示用户比赛结束。
效果: 和下面的功能一起展示
1.5 显示比赛成绩 成员函数添加
实现步骤:
- 在speechManager.h中提供比赛的的成员函数
void showScore();
- 在speechManager.cpp中实现成员函数
void showScore();
- 最后在
void SpeechManager::startSpeech()
中加上开始比赛成员函数,并在基于SCL的演讲比赛流程管理系统.cpp中测试
代码展示:
//显示比赛成绩
void SpeechManager::showScore()
{
cout << endl << string(15, '*') << " 第 " << this->m_index << " 轮比赛晋级选手如下 " << string(15, '*') << endl;
vector<int> vspeech;
if (this->m_index == 1)
{
vspeech = v2;
}
else
{
vspeech = vfinal;
}
for (vector<int>::iterator it = vspeech.begin(); it != vspeech.end(); it++)
{
cout << "选手编号:" << *it << " 选手姓名:" << this->m_Speaker[*it].m_Name
<< " 选手成绩:" << this->m_Speaker[*it].m_Score[this->m_index - 1] << endl;
}
cout << endl;
system("pause");
system("cls");
this->showMenu();
}
//开始比赛 整个比赛流程的控制函数
void SpeechManager::startSpeech()
{
//第一轮比赛开始
//抽签
this->speechDraw();
//比赛
this->speechContest();
//显示晋级结果
this->showScore();
//第二轮比赛开始
this->m_index++;
//抽签
speechDraw();
//比赛
this->speechContest();
//显示晋级结果
this->showScore();
//保存分数
}
注意点:
通过m_index来控制第几轮比赛的成绩的输出,根据不同的容器和比赛的轮数,做不同的输出,具体如下:
- 如果m_index=1,第一轮比赛,获取第一轮比赛晋级的选手,在v2中;
- 如果m_index=2,第二轮比赛,获取第二轮比赛晋级的选手,最终胜出选手,在vfinal中;
- 选手成绩都存放在m_Speaker中,通过选手编号索引到选手的成绩;
- 最后清屏并返回菜单界面。
效果:
1.6 保存分数函数添加
功能描述: 将每次演讲比赛的得分记录到文件中
实现步骤:
- 在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 = vfinal.begin(); it != vfinal.end(); it++)
{
//csv文件用逗号分割 m_Score[1]最终得分
ofs << *it << "," << this->m_Speaker[*it].m_Score[1] << ",";
}
//关闭
ofs.close();
cout << "当届比赛记录已保存!" << endl;
}
//开始比赛 整个比赛流程的控制函数
void SpeechManager::startSpeech()
{
//第一轮比赛开始
//抽签
this->speechDraw();
//比赛
this->speechContest();
//显示晋级结果
this->showScore();
//第二轮比赛开始
this->m_index++;
//抽签
speechDraw();
//比赛
this->speechContest();
//显示晋级结果
this->showScore();
//保存分数
this->saveRecord();
cout << "本届比赛正式结束!" << endl;
system("pause");
system("cls");
cout << endl;
}
注意点:
- 用csv文件保存,csv文件用逗号分隔,每一届成绩保存完之后要换行
- 文件内容保存时,用
ios::app
这个方式追加保存 - 最后提示用户文件保存完毕
效果: