← 课程目录
第 18 讲 模块五:批量处理数据

实战:班级成绩统计

18 / 24
💡
本节课目标
模块五最后一讲,不学新语法,把 vector、循环遍历、累加、找最大值全部用进一个完整程序: 先输入班级人数,再逐个录入成绩,最后输出总分、平均分、最高分
开场

模块五回顾

📖 理论讲解

第 14~17 讲

第 14 讲:数组,用下标集中管理一批数据,但长度声明后就定死了。

第 15 讲:循环 + 数组,用循环变量当下标遍历、求和、打擂台找最大值。

第 16 讲:vector,不用提前定长,push_back 随时加、.size() 随时知道有多少个。

第 17 讲:字符串其实也遵循同一套下标、遍历规则。

今天把 vector、遍历、求和、找最大值拼在一起,做一个真正有用的小工具。

需求

程序要做什么

📖 理论讲解

功能说明

  • 先输入这个班一共有多少人,人数必须是正整数。
  • 逐个输入每个人的成绩,用 vector 存起来,成绩范围是 0~100
  • 输出总分
  • 输出平均分
  • 输出最高分
第一步

先把录入和遍历跑通

📖 理论讲解

人数不固定,正是 vector 的主场

班级人数运行时才知道,用数组不合适——这正是第 16 讲学 vector 的原因。第一步先只做“录入 + 原样打印回顾”,确认这部分没问题,再往上加统计功能:

roster_step1.cpp CPP
#include <cstddef>
#include <iostream>
#include <vector>

int main() {
    int n;
    std::cout << "这个班有多少人?";
    if (!(std::cin >> n) || n <= 0) {
        std::cout << "人数必须是正整数。" << std::endl;
        return 1;
    }

    std::vector<int> scores;
    for (int i = 0; i < n; i++) {
        int s;
        std::cout << "请输入第 " << (i + 1) << " 个人的分数:";
        if (!(std::cin >> s) || s < 0 || s > 100) {
            std::cout << "成绩必须是 0 到 100 之间的整数。" << std::endl;
            return 1;
        }
        scores.push_back(s);
    }

    std::cout << "录入完毕,一共 " << scores.size() << " 个分数:" << std::endl;
    for (int score : scores) {
        std::cout << score << " ";
    }
    std::cout << std::endl;

    return 0;
}
💡
为什么先存起来,而不是边输入边算
总分和最高分其实都能在输入时顺手更新,平均分最后再用总分除以人数即可; 完成这三项统计并不一定需要 vector。 这里保存全部成绩,是为了练习本模块的容器与遍历,也方便原样打印、反复统计, 以及完成后面的“输出所有高于平均分的成绩”等二次分析。
输入也要验证
std::cin >> n 可以直接放进 if 条件: 读取成功时条件成立,输入字母或遇到文件结束时条件失败。 程序还会检查人数必须大于 0、成绩必须在 0~100, 从源头保证后面的平均分和 scores[0] 都有意义。
⚡ 动手实操 约 5 分钟

先跑通录入这一步

roster_step1.cpp 跑起来,输入人数为 3,再输入 3 个分数, 确认最后打印出来的和你输入的一致。

第二步

求总分和平均分

📖 理论讲解

累加器套路,和第 15 讲一样

有了装满数据的 scores,求和就是熟悉的累加器遍历,循环条件用 .size() 而不是写死的数字:

roster_step2.cpp CPP
int sum = 0;
for (int score : scores) {
    sum += score;
}
double average = sum / static_cast<double>(scores.size());

std::cout << "总分:" << sum << std::endl;
std::cout << "平均分:" << average << std::endl;
static_cast<double> 是什么
scores.size() 返回的类型不是普通 int,直接用 sum / scores.size() 容易踩到“整数除法截断小数”的坑。 static_cast<double>(scores.size()) 会先把人数明确转换成 double 再参与除法,确保平均分带小数。 它比 C 风格的 (double) 写法更清楚,也更符合现代 C++ 的习惯。
第三步

找最高分

📖 理论讲解

打擂台,原封不动搬过来

第 15 讲的打擂台算法,数据结构从数组换成 vector, 代码几乎不用改

roster_step3.cpp CPP
int maxScore = scores[0];
for (std::size_t i = 1; i < scores.size(); i++) {
    if (scores[i] > maxScore) {
        maxScore = scores[i];
    }
}

std::cout << "最高分:" << maxScore << std::endl;
实现

拼起来:完整代码

class_roster.cpp CPP
#include <cstddef>
#include <iostream>
#include <vector>

int main() {
    int n;
    std::cout << "这个班有多少人?";
    if (!(std::cin >> n) || n <= 0) {
        std::cout << "人数必须是正整数。" << std::endl;
        return 1;
    }

    std::vector<int> scores;
    for (int i = 0; i < n; i++) {
        int s;
        std::cout << "请输入第 " << (i + 1) << " 个人的分数:";
        if (!(std::cin >> s) || s < 0 || s > 100) {
            std::cout << "成绩必须是 0 到 100 之间的整数。" << std::endl;
            return 1;
        }
        scores.push_back(s);
    }

    int sum = 0;
    for (int score : scores) {
        sum += score;
    }
    double average = sum / static_cast<double>(scores.size());

    int maxScore = scores[0];
    for (std::size_t i = 1; i < scores.size(); i++) {
        if (scores[i] > maxScore) {
            maxScore = scores[i];
        }
    }

    std::cout << "----------------------" << std::endl;
    std::cout << "总分:" << sum << std::endl;
    std::cout << "平均分:" << average << std::endl;
    std::cout << "最高分:" << maxScore << std::endl;

    return 0;
}
逐段看

整个程序只用了一个 vector 存所有成绩,之后的求和、 求平均、找最高分,全都基于这一份数据反复遍历——录入一次,统计三次。

三段统计代码是相互独立的:删掉找最高分那一段,求和平均分照样能跑, 这也是分步骤搭建的好处——每一步都能单独验证。

测试

换几种班级规模试试

⚡ 动手实操 约 15 分钟

按清单逐项验证

跑起来,依次测试下面的场景,确认结果正确:

场景期望表现
人数为 1,输入一个分数总分、平均分、最高分都等于这个分数
人数为 5,分数各不相同最高分是其中最大的那个
人数为 5,全部输入同一个分数最高分等于这个分数,平均分也等于它
人数为 0、负数或字母提示“人数必须是正整数”,安全结束
成绩为 -1、101 或字母提示合法范围是 0 到 100,安全结束
有多个并列最高分最高分数值仍然正确
💡
边界最容易错
“人数为 0”和“人数为 1”都很容易漏测。前者应该在统计前被拒绝, 后者则应该正常完成;这两个场景能一起检验平均分除法与 scores[0] 是否安全。
拓展

让统计更全面

练习 18-1

加一个最低分

仿照找最高分的打擂台算法,再加一段找最低分并输出。

练习 18-2

统计及格人数

遍历 scores,统计并输出及格(大于等于 60 分)的人数

提示:需要一个计数器变量,遍历时符合条件就加一,回忆第 9 讲累加的写法。

练习 18-3

打印高于平均分的名单

算出平均分后,再遍历一次 scores,把所有高于平均分的成绩打印出来。

提示:这需要两轮遍历——第一轮算出平均分,第二轮才能拿平均分去比较, 顺序不能颠倒。

练习 18-4

在线统计:不用 vector

另写一个版本,不保存全部成绩:每读入一个成绩,就更新总分和最高分, 最后再计算平均分。比较两个版本,说明为什么这个版本不能在统计结束后 再打印所有高于平均分的原始成绩。

小结

模块五回顾

📖 理论讲解

第 14~18 讲,你掌握了

讲次核心内容
第 14 讲数组声明、下标从 0 开始、读写元素
第 15 讲循环遍历数组、求和平均分、打擂台找最大 / 最小值
第 16 讲vectorpush_back.size()
第 17 讲字符串的 .length()、下标、遍历
第 18 讲综合实战:vector + 遍历统计

到这里,程序已经能批量存储数据、批量处理数据—— 这是写“真正有用”的程序离不开的能力。

💡
下节课预告
现在“一个人的信息”还是分散在好几个变量或好几个 vector 里 (比如姓名和分数各存各的)。模块六学 struct, 把姓名、分数这些相关数据打包成一个整体,再综合前五个模块的内容, 完成两个综合项目。