举办歌手大奖赛。
设计歌手类,包括:编号、姓名、各评委打分等属性。
要求功能:
可以打印当前最高、最低分选手属性;
打印已出场人数;
可以按照平均分由高到低打印已出场选手属性;
写出main函数中模拟n个选手出场的过程;
要求:1.使用静态成员。
说明:
- 由于不太清楚题意这个“打印当前最高、最低分选手属性”,表意到底是打印某个选手的最高分/最低分 还是打印所有选手中最高/最低的选手的信息,所以全部都写了出来。
- 另外,由于 “可以按照平均分由高到低打印已出场选手属性” 这个题目要求,出现在了“要求功能”里面,所以感觉应该是写一个类的函数来打印。而不是在main函数中手动实现。
- 开整!
依据要求的功能,定义的类如下:
//Singer.h
#include<string>
#include<vector>
using namespace std;
class Singer
{
public:
Singer(string pName, string pID); /*构造函数初始化名字,编号等,确定pNext*/
float addMarks(); /*加入各评委分数,计算并存储、返回平均值*/
void printMax(); /*打印某个选手的最高分*/
void printMin(); /*打印某个选手最低分*/
static void printPeoNumber(); /*可打印已出场人数*/
static void printAllMax(); /*打印最高分的选手的属性*/
static void printAllMin(); /*打印最低分选手的属性*/
static void orderPrint(); /*按平均分大小打印选手属性*/
~Singer();
private:
static int nuOfPeople; /*记录出场的人数*/
static Singer* pFirst; /*整个类的头指针*/
Singer* pNext; /*指向下一个节点,链表*/
string name; /*储存名字*/
string ID; /*储存编号*/
vector<float> marks; /*用容器vector,动态化存储各评委的分数*/
int order; /*记录出场顺序而已,后面为用到*/
float average; /*储存选手的平均分*
};
1.为实现属于整个类的——“按平均分从大到小,打印选手属性”的功能,首先想到的还是链表,当每声明一个新的该类对象的时候,将新声明的这个对象自动变为链表的节点。
2.与上次MyVector类练习,应用的链表不同之处在于:上次链表出现是在,每一个MyVector类的对象的成员中,而这次我们是要将声明的对象作为节点。
3.为动态存储评委分数,采用了vector容器,之后运用的vector的方法有:
方法 | 作用 |
---|---|
v.size() | 返回容器中实际数据的个数 |
v.empty() | 判断容器是否为空,空为true |
c.push_back(elem) | 在尾部加入一个数据 |
在 Singer.cpp 文件中各函数实现如下:
//Singer.cpp 文件中引入的头文件
#include<iostream>
#include "Singer.h"
using namespace std;
/*构造函数初始化名字,编号等,确定pNext*/
Singer::Singer(string pName,string pID)
{
nuOfPeople++;
name = pName;
ID = pID;
order = nuOfPeople;
average = 0;
pNext = pFirst; //尾插法创建链表
pFirst = this;
}
/*加入各评委分数,计算并存储、返回平均值*/
float Singer::addMarks()
{
static int number = 0; /*利用静态局部变量生命期直到程序结束的特性,只在第一次调用addMarks函数时输入评委个数*/
if (number == 0) { /*大致约等于第一次执行函数时 才输入评委人数 (ˉ▽ˉ;) */
cout << "请输入评委位数:";
cin >> number;
}
cout << "请开始输入" << name << "的分数:" << endl;
for (int i = 0; i < number; i++) {
float mark;
cin >> mark;
marks.push_back(mark);
}
cout << "--------------------------------------------------" << endl; //分隔开每次输入分数,在控制台显示更分明
float sum = 0; /*输入分数后直接计算平均分*/
for (int i = 0; i < marks.size(); i++)
sum += marks[i];
average = sum / marks.size();
return average;
}
*可打印已出场人数*/
void Singer::printPeoNumber()
{
cout << "出场人数是:" << nuOfPeople<<endl;
}
*打印某个选手的最高分*/
void Singer::printMax()
{
if (marks.empty())
cout << "Empty!" << endl;
else {
float max = marks[0];
for (int i = 0; i < marks.size(); i++) / /遍历所有分数找出最高分
if (marks[i] > max)
max = marks[i];
cout << "选手"<<name<<"的最高分为:" << max << endl;
}
}
/*打印某个选手最低分*/
void Singer::printMin()
{
if (marks.empty()) //先判断有没有分
cout << "Empty" << endl;
else {
float min = marks[0];
for (int i = 0; i < marks.size(); i++) //便历所有分数找出最低分
if (marks[i] <min)
min = marks[i];
cout << "选手"<<name<<"的最低分为:" << min << endl;
}
}
/*打印最高分的选手的属性*/
void Singer::printAllMax()
{
if (pFirst == nullptr) { //先判断链表有没有节点,pFirst是不是为空,避免对空指针引用(主要是为了打消编译器的警告⚠+▽+)。
cout << "链表为空!" << endl;
return;
}
float max = pFirst->average;
Singer* pMax=pFirst; //用于标记最大值节点
for (Singer* pS = pFirst;pS; pS = pS->pNext) { //遍历链表找出最大值
if (pS->average > max) {
max = pS->average;
pMax = pS;
}
}
for (Singer* pSame=pFirst;pSame;pSame=pSame->pNext) { // 遍历一遍防止有分数相同的情况出现
if(pSame->average==pMax->average){
cout << "当前最高分选手姓名" << pSame->name << " 编号" << pSame->ID << endl;
cout << "各评委打分为:" << endl;
for (int i = 0; i < pSame->marks.size(); i++)
cout << pSame->marks[i] << " ";
cout << endl << endl; // 为了输出好看
}
}
cout << "——————————————————————————————————————————————————————————" << endl;
}
/*打印最低分选手的属性*/
void Singer::printAllMin()
{
if (pFirst == nullptr) {
cout << "链表为空!" << endl;
return;
}
for (Singer* pS = pFirst; pS; pS = pS->pNext) { //遍历找出最大值
if (pS->average < min) {
min = pS->average;
pMin = pS;
}
}
for (Singer* pSame = pFirst; pSame; pSame = pSame->pNext) { //遍历一遍,防止有分数相同的情况出现
if (pSame->average==pMin->average) {
cout << "当前最低分选手姓名" << pSame->name << " 编号" << pSame->ID << endl;
cout << "各评委打分为:" << endl;
for (int i = 0; i < pSame->marks.size(); i++)
cout << pSame->marks[i] << " ";
cout << endl << endl; //为了输出好看
}
}
}
/*按平均分大小打印选手属性*/
void Singer::orderPrint()
{
if (pFirst == nullptr) {
cout << "链表为空!" << endl;
return;
}
static float premax = 1000; //设置为1000主要是想给他一个十分大的初始值利用静态局部变量只初始化一次,生命期直到程序结束的特性,记录找出的以前的的最大值,
int i = 0; //i记录打印了的个数
while(i!=nuOfPeople) { //记录输出的次数
float max = 0;
Singer* pMax = nullptr;
for (Singer* pS = pFirst; pS; pS = pS->pNext) { //遍历找出单次最大值,注意每次找出的最大值要小于上一次
if ((pS->average > max)&&(pS->average<premax)) {
max = pS->average;
pMax = pS;
}
}
int rank = i; //引入为解决每次可能有相同分数出现的排名问题
for (Singer* pSame = pFirst; pSame; pSame = pSame->pNext) { //遍历一次防止出现两个平均值相同的情况,同样小心空链表时候的对nullptr引用
if (pMax->average == pSame->average) { //按格式打印
i++; //更新打印次数
cout << "排名:" << rank+1<< " 姓名" << pSame->name << " 编号" << pSame->ID << endl;
cout << "各评委打分为:" << endl;
for (int k = 0; k < pSame->marks.size(); k++)
cout << pSame->marks[k] << " ";
cout << endl;
cout << "均分为:" << pSame->average << endl;
cout << "______________________________________________" << endl; //分隔符
}
} /*for*/
premax = max; /*记录上一次的最大值,下一次的最大值要小于这个值*/
}/*while*/
premax = 1000.0; 由于静态局部变量值初始化一次 重置premax防止当在程序中第二次调用函数时 比大小 出现问题
}
/*析构,减去人数*/
Singer::~Singer()
{
nuOfPeople--;
if (pFirst == this) //当删除的处于头部的对象的时候
{
pFirst = pNext;
return;
}
for (Singer* pS = pFirst; pS; pS = pS->pNext) //当删除的对象位于链表中间的时候
if (pS->pNext == this)
{
pS->pNext = pNext;
return;
}
}
对于析构函数的说明:
把析构函数写成这样,主要是为了避免当创建的对象作用域为块作用域的时候,出了块,对象被销毁,导致链表断裂,所以在对象被销毁的时候,也应该调整链表。
最后测试类的功能,模拟歌手出场顺序:
//mian.cpp
#include<iostream>
#include<string>
#include"Singer.h"
using namespace std;
int Singer::nuOfPeople = 0;
Singer* Singer::pFirst = nullptr;
int main()
{
/*模拟选手出场顺序*/
Singer a("zhang","12000"), b("sun", "12001"), c("li", "12002"), d("zhou", "12003"),e("kou","12004");
a.addMarks();
b.addMarks();
c.addMarks();
d.addMarks();
e.addMarks();
cout << endl;
Singer::printPeoNumber();
cout << endl;
d.printMax();
d.printMin();
cout << endl;
Singer::orderPrint();
Singer::printAllMax();
Singer::printAllMin();
return 0;
}
注意事项:
类的静态成员一定要记住初始化,在初始化的时候才能分配空间,不然过不了编译器:
上述main函数执行结果如下:
测试使用的数据有:
5
1 2 3 4 5
9 9 9 9 9
9 9 9 9 9
1 1 1 1 1
1 1 1 1 1
5
1 2 3 4 5
8 2 6 4 2
8 3 4 7 5
3 4 6 8 2
1 2 4 9 8
5
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
总结:
在一些打印数据的函数,比如printAllMax( )、printAllMin()、orderPrint(),打印部分本可以写成一个函数来复用,以此减少代码量,使程序更简洁。由于想偷懒就不重构了,hhh。
就这样,给大伙儿整点儿烂活。