03 学生成绩管理系统
03 学生成绩管理系统
作者:abnb250/山中鹤鸣
博客:https://www.cnblogs.com/abnb250
交流群:1062464078
点击返回目录
前情提要
这一节用 Score + Student + std::unordered_map 搭一个命令行成绩管理小程序,支持增删改查、按总分/均分/单科排序、班级统计、文件读写。
注释版盯住几处易踩的坑:
get_score 用 -1 表示未找到,因为 0 是合法成绩;
set_score 必须按引用遍历 Score,否则改的是副本;
op4 的链式 set_* 调用依赖每个 setter 返回 this;
op5 的 classmin 初值不能是 -1,且 classsum / (1.0)numss 实际是乘法而非除法。
代码(加注释版)
注释解释了两种场景:
- 代码有反直觉痕迹,自然写法就在旁边。
- 代码看起来自然,但背后有隐形决策。
Score.h
#ifndef SCORE_H
#define SCORE_H
#include <iostream>
#include <fstream>
#include <cmath>
#include <algorithm>
#include <string>
#include <vector>
using std::string, std::vector;
class Score{
private:
string kind;
double grade;
public:
Score(): kind("unknown"), grade(0.0){}
Score(string k, double g) : kind(k), grade(g) {}
string get_kind() const;
double get_grade() const;
void set_score(double newgra);
};
#endif
Score.cpp
#include "Score.h"
string Score::get_kind() const{
return kind;
}
double Score::get_grade() const{
return grade;
}
void Score::set_score(double newgra){
grade = newgra;
return;
}
Student.h
#ifndef STUDENT_H
#define STUDENT_H
#include <iostream>
#include <fstream>
#include <cmath>
#include <algorithm>
#include <string>
#include <vector>
#include "Score.h"
using std::string;
using std::vector;
class Student{
private:
string num;
string name;
char sex;
int age;
string cla;
vector<Score> v;
double sum;
double ave;
public:
Student() : num("unknown"), name("unknown"), sex('b'), age(18), cla("7012") {}
Student(string nu, string na, char se, int ag, string cl) : num(nu), name(na), sex(se), age(ag), cla(cl) {}
void show_score();
double get_score(string kindname) const;
string get_name();
void showall();
Student& set_num(string newnum);
Student& set_name(string newname);
Student& set_sex(char newsex);
Student& set_age(int newage);
Student& set_cla(string newcla);
bool set_score(string newkind, double newgrade);
Student& add_sco(Score sc);
double get_sum() const;
double get_ave() const;
void fileall(std::fstream& file);
};
bool cmpsum(const Student& a, const Student& b);
bool cmpave(const Student& a, const Student& b);
#endif
Student.cpp
#include "Student.h"
void Student::show_score(){
using namespace std;
if(v.size() == 0){
cout << "no score" << '\n';
return;
}
for(Score x : v){
cout << x.get_kind() << ": " << x.get_grade() << '\n';
}
cout << "===========" << '\n';
return;
}
double Student::get_score(string kindname) const{
for(Score x : v){
if(x.get_kind() == kindname){
return x.get_grade();
}
}
// 不能改成返回 0 表示未找到,0 是合法成绩,调用方无法区分“没这门课”和“考了 0 分”
return -1;
}
string Student::get_name(){
return name;
}
void Student::showall(){
using namespace std;
// string num;
// string name;
// char sex;
// int age;
// string cla;
// vector<Score> v;
// double sum;
// double ave;
cout << "========================" << '\n';
cout << "number: " << num << '\n';
cout << "name: " << name << '\n';
cout << "sex: " << sex << '\n';
cout << "age: " << age << '\n';
cout << "class:" << cla << '\n';
show_score();
cout << "summary: " << get_sum() << '\n';
cout << "average" << get_ave() << '\n';
cout << "========================" << '\n';
}
Student& Student::set_num(string newnum) {
num = newnum;
return *this;
}
Student& Student::set_name(string newname){
name = newname;
return *this;
}
Student& Student::set_sex(char newsex){
sex = newsex;
return *this;
}
Student& Student::set_age(int newage){
age = newage;
return *this;
}
Student& Student::set_cla(string newcla){
cla = newcla;
return *this;
}
bool Student::set_score(string newkind, double newgrade){
using namespace std;
if(v.size() == 0){
cout << "nofind" << '\n';
return false;
}
// 不能按值遍历 Score x,修改的是副本,原 vector 里的成绩不变
for(Score &x : v){
if(x.get_kind() == newkind){
x.set_score(newgrade);
return true;
}
}
cout << "nofind" << '\n';
return false;
}
Student& Student::add_sco(Score sc) {
v.push_back(sc);
return *this;
}
double Student::get_sum() const{
double sum = 0;
for(Score x : v){
sum += x.get_grade();
}
return sum;
}
double Student::get_ave() const{
double sum = get_sum();
double nums = static_cast<double>(v.size());
if(nums == 0){
std::cout << "/ 0" << '\n';
return 0.0;
}
return sum/nums;
}
bool cmpsum(const Student& a, const Student& b){
return a.get_sum() > b.get_sum();
}
bool cmpave(const Student& a, const Student& b){
return a.get_ave() > b.get_ave();
}
void Student::fileall(std::fstream& file){
using namespace std;
file << "========================" << '\n';
file << "number: " << num << '\n';
file << "name: " << name << '\n';
file << "sex: " << sex << '\n';
file << "age: " << age << '\n';
file << "class:" << cla << '\n';
if(v.size() == 0){
file << "no score" << '\n';
}
else{
for(Score x : v){
file << x.get_kind() << ": " << x.get_grade() << '\n';
}
}
file << "summary: " << get_sum() << '\n';
file << "average" << get_ave() << '\n';
file << "========================" << '\n';
}
system.cpp
#include "Student.h"
#include "Score.h"
#include <map>
#include <unordered_map>
#include <vector>
#include <fstream>
std::unordered_map<string, Student> maps;
int Showmain(){
using namespace std;
cout << "========================" << '\n';
cout << "[1]show students" << '\n';
cout << "[2]add a student" << '\n';
cout << "[3]delete a student" << '\n';
cout << "[4]set a student" << '\n';
cout << "[5]show the class" << '\n';
cout << "[6]read from file" << '\n';
cout << "[7]save into a file" << '\n';
cout << "[0]exit" << '\n';
cout << "========================" << '\n';
int choice;
// 如果 cin>>choice 失败(如输入非数字),choice 未初始化,返回后 switch 使用未初始化值 UB;需验证输入校验
cin >> choice;
return choice;
}
void op1(){
using namespace std;
while(1){
cout << "========================" << '\n';
cout << "[a]just a student" << '\n';
cout << "[b]show all with summary" << '\n';
cout << "[c]show all with average" << '\n';
cout << "[d]show all with subject" << '\n';
cout << "[q]exit" << '\n';
char choice;
cin >> choice;
if(choice == 'q') break;
if(choice == 'a'){
cout << "cin number and name" << '\n';
string nums, nams;
cin >> nums >> nams;
auto it = maps.find(nums);
if(it == maps.end()){
cout << "no find" << '\n';
continue;
}
if(it->second.get_name() != nams){
cout << "name is error" << '\n';
continue;
}
it->second.showall();
}
if(choice == 'b'){
vector<Student> vs;
for(auto it : maps){
vs.push_back(it.second);
}
sort(vs.begin(), vs.end(), cmpsum);
for(auto x : vs){
x.showall();
}
}
if(choice == 'c'){
vector<Student> vs;
for(auto it : maps){
vs.push_back(it.second);
}
sort(vs.begin(), vs.end(), cmpave);
for(auto x : vs){
x.showall();
}
}
if(choice == 'd'){
cout << "cin the subject" << '\n';
string sub;
cin >> sub;
vector<Student> vs;
for(auto it : maps){
vs.push_back(it.second);
}
sort(vs.begin(), vs.end(),[&sub](const Student& a, const Student& b){
return a.get_score(sub) > b.get_score(sub);
});
for(auto x : vs){
x.showall();
}
}
}
}
void op2(){
using namespace std;
cout << "cin the basic message: " << '\n';
// string num;
// string name;
// char sex;
// int age;
// string cla;
cout << "number(string), name(string), sex(char), age(int), class(string)." << '\n';
string nums, nams, clas;
char sexs;
int ages;
cin >> nums >> nams >> sexs >> ages >> clas;
cout << "everything is ok" << '\n';
Student tmp(nums, nams, sexs, ages, clas);
maps[nums] = tmp;
return;
}
void op3(){
using namespace std;
cout << "cin number" << '\n';
string tmp;
cin >> tmp;
maps.erase(tmp);
}
void op4(){
using namespace std;
while(1){
cout << "========================" << '\n';
cout << "[a]basic message" << '\n';
cout << "[b]subject score" << '\n';
cout << "[q]exit" << '\n';
cout << "========================" << '\n';
char ch;
cin >> ch;
if(ch == 'q') break;
if(ch == 'a'){
cout << "cin the number and the basic message" << '\n';
cout << "number(string), name(string), sex(char), age(int), class(string)." << '\n';
string nums, nams, clas;
char sexs;
int ages;
cin >> nums >> nams >> sexs >> ages >> clas;
cout << "everything is ok" << '\n';
// Student& set_num(string newnum);
// Student& set_name(string newname);
// Student& set_sex(char newsex);
// Student& set_age(int newage);
// Student& set_cla(string newcla);
auto it = maps.find(nums);
if(it == maps.end()){
cout << "no find" << '\n';
continue;
}
// 这里依赖 set_name/set_sex/set_age/set_cla 返回 *this,改成 void 会编译失败
it->second.set_name(nams).set_sex(sexs).set_age(ages).set_cla(clas);
}
if(ch == 'b'){
cout << "cin the number" << '\n';
string tname;
cin >> tname;
auto it = maps.find(tname);
if(it == maps.end()){
cout << "no find" << '\n';
continue;
}
cout << "cin the subject name and score" << '\n';
string sname;
double sc;
cin >> sname >> sc;
bool ok = it->second.set_score(sname, sc);
if(!ok){
Score ns(sname, sc);
it->second.add_sco(ns);
}
}
}
}
void op5(){
using namespace std;
cout << "========================" << '\n';
//支持统计班级总分、平均分、最高分、最低分
double classsum = 0.0, classave, classmax = -1.0, classmin = -1.0;
int numss = 0;
for(auto it : maps){
classsum += it.second.get_sum();
classmax = max(classmax, it.second.get_sum());
// 不能把 classmin 初始化为 -1,总分非负,min(-1, sum) 恒为 -1,最小值统计错误
classmin = min(classmin, it.second.get_sum());
numss++;
}
// 不能写成 classsum / (1.0)*numss,乘除同优先级左结合,实际是 classsum*numss,平均分错误
classave = classsum / (1.0)*numss;
cout << "class summary: " << classsum << '\n';
cout << "class average: " << classave << '\n';
cout << "class max: " << classmax << '\n';
cout << "class min: " << classmin << '\n';
cout << "========================" << '\n';
}
void op6(){
using namespace std;
cout << "cin the file name" << '\n';
string fname;
cin >> fname;
fstream file(fname, ios::in | ios:: out);
if(!file.is_open()){
cout << "file open filed" << '\n';
return;
}
string tmp;
while(getline(file, tmp)){
cout << tmp << '\n';
}
file.close();
}
void op7(){
using namespace std;
cout << "cin the file name" << '\n';
string fname;
cin >> fname;
// 用 ios::in|ios::out 打开,文件不存在时不会创建;若改成 ios::out 会截断旧内容,需验证保存语义
fstream file(fname, ios::in|ios::out);
if(!file.is_open()){
cout << "file open is error" << '\n';
return;
}
for(auto it : maps){
it.second.fileall(file);
}
file.close();
}
void op0(){
using namespace std;
cout << "========================" << '\n';
cout << "welcome to use next time" << '\n';
cout << "wish you have a goot day" << '\n';
cout << "========================" << '\n';
}
int main(){
using namespace std;
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
while(1){
int choice = Showmain();
switch(choice){
case 1: op1(); break;
case 2: op2(); break;
case 3: op3(); break;
case 4: op4(); break;
case 5: op5(); break;
case 6: op6(); break;
case 7: op7(); break;
case 0: op0(); break;
}
if(choice == 0) break;
}
return 0;
}
/*
我要这天再遮不住我眼,我要这地再埋不住我心。
交流群:1062464078
*/

浙公网安备 33010602011771号