C++(学习) —— Vector容器,类的静态成员的使用练习(Singer类)

举办歌手大奖赛。
设计歌手类,包括:编号、姓名、各评委打分等属性。
要求功能:
可以打印当前最高、最低分选手属性;
打印已出场人数;
可以按照平均分由高到低打印已出场选手属性;
写出main函数中模拟n个选手出场的过程;
要求:1.使用静态成员。

说明:

  1. 由于不太清楚题意这个“打印当前最高、最低分选手属性”,表意到底是打印某个选手的最高分/最低分 还是打印所有选手中最高/最低的选手的信息,所以全部都写了出来。
  2. 另外,由于 “可以按照平均分由高到低打印已出场选手属性” 这个题目要求,出现在了“要求功能”里面,所以感觉应该是写一个类的函数来打印。而不是在main函数中手动实现。
  3. 开整!

依据要求的功能,定义的类如下:

//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。
就这样,给大伙儿整点儿烂活。在这里插入图片描述

  • 6
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值