OOP-实验6

实验任务1

源代码task1

 1 // 结构体Contestant定义及其重载运算符函数>>和<<实现
 2 
 3 #pragma once
 4 
 5 #include <iomanip>
 6 #include <iostream>
 7 #include <string>
 8 
 9 struct Contestant
10 {
11     long id;           // 学号
12     std::string name;  // 姓名
13     std::string major; // 专业
14     int solved;        // 解题数
15     int penalty;       // 总罚时
16 };
17 
18 // 重载<<
19 // 要求:姓名/专业里不含空白符
20 inline std::ostream &operator<<(std::ostream &out, const Contestant &c)
21 {
22     out << std::left;
23     out << std::setw(15) << c.id
24         << std::setw(15) << c.name
25         << std::setw(15) << c.major
26         << std::setw(10) << c.solved
27         << std::setw(10) << c.penalty;
28 
29     return out;
30 }
31 
32 // 重载>>
33 inline std::istream &operator>>(std::istream &in, Contestant &c)
34 {
35     in >> c.id >> c.name >> c.major >> c.solved >> c.penalty;
36 
37     return in;
38 }
contestant.hpp
 1 // 工具函数实现(排序函数、数据读/写)
 2 
 3 #pragma once
 4 
 5 #include <fstream>
 6 #include <iostream>
 7 #include <stdexcept>
 8 #include <string>
 9 #include <vector>
10 
11 #include "contestant.hpp"
12 
13 // ACM 排序规则:先按解题数降序,再按罚时升序
14 inline bool cmp_by_solve(const Contestant &a, const Contestant &b)
15 {
16     // 先按解题数降序
17     if (a.solved != b.solved)
18         return a.solved > b.solved;
19 
20     // 再按罚时升序
21     return a.penalty < b.penalty;
22 }
23 
24 // 将结果写至任意输出流
25 inline void write(std::ostream &os, const std::vector<Contestant> &v)
26 {
27     for (const auto &x : v)
28         os << x << '\n';
29 }
30 
31 // 将结果打印到屏幕
32 inline void print(const std::vector<Contestant> &v)
33 {
34     write(std::cout, v);
35 }
36 
37 // 将结果保存到文件
38 inline void save(const std::string &filename, const std::vector<Contestant> &v)
39 {
40     std::ofstream os(filename);
41     if (!os)
42         throw std::runtime_error("fail to open " + filename);
43 
44     write(os, v);
45 }
46 
47 // 从文件读取信息(跳过标题行)
48 inline std::vector<Contestant> load(const std::string &filename)
49 {
50     std::ifstream is(filename);
51     if (!is)
52         throw std::runtime_error("fail to open " + filename);
53 
54     std::string line;
55     std::getline(is, line); // 跳过标题
56 
57     std::vector<Contestant> v;
58     Contestant t;
59     int seq;
60     while (is >> seq >> t)
61         v.push_back(t);
62 
63     return v;
64 }
utils.hpp
 1 // 应用代码 + main
 2 
 3 #include <algorithm>
 4 #include <iostream>
 5 #include <stdexcept>
 6 #include <vector>
 7 
 8 #include "contestant.hpp"
 9 #include "utils.hpp"
10 
11 const std::string in_file = "./data.txt";
12 const std::string out_file = "./ans.txt";
13 
14 void app()
15 {
16     std::vector<Contestant> contestants;
17 
18     try
19     {
20         contestants = load(in_file);
21         std::sort(contestants.begin(), contestants.end(), cmp_by_solve);
22         print(contestants);
23         save(out_file, contestants);
24     }
25 
26     catch (const std::exception &e)
27     {
28         std::cerr << e.what() << '\n';
29         return;
30     }
31 }
32 
33 int main()
34 {
35     app();
36 }
task1.cpp

运行测试截图

task1_1

task1_2

回答问题

问题1:流操作与代码复用

观察 print() 与 save() 的实现,均在内部调用 write():

(1)write() 的参数类型是 std::ostream&,它为什么能同时接受 std::cout 和 std::ofstream 对象作为实参?

原因:std::ostream 是 std::cout 和 std::ofstream 的基类,而基类的引用可以和任意派生类对象绑定;

I/O 流类继承层次关系:

image

(2)如果要把结果写到其他设备,只要该设备也提供 std::ostream 接口,还需改动 write() 吗?

不需要改动 write() ,体现了代码复用性

问题2:异常处理与捕获

在代码中找到两处 throw 语句,说明:

(1)什么情况下会抛出异常;

save():throw std::runtime_error("fail to open " + filename);  打开文件 filename 失败时抛出异常

load():throw std::runtime_error("fail to open " + filename);   打开文件 filename 失败时抛出异常

(2)异常被谁捕获、做了哪些处理。

两处异常会被 task1.cpp 中的 void app() 函数捕获;

处理:std::cerr << e.what() << '\n'; return;  输出异常信息并退出 app() ,不再进行后续操作

问题3:替代写法

函数 app 中 std::sort(contestants.begin(), contestants.end(), cmp_by_solve); 参数 cmp_by_solve 换成下面的 lambda 表达式是否可以?功能、性能、结果是否一致?

可以替换;

且功能、性能、结果一致,都能实现先按解题数降序、再按罚时升序的排序逻辑

问题4:数据完整性与代码健壮性

把 in_file 改成 "./data_bad.txt" (内含空白行或字段缺失),重新编译运行:

(1)观察运行结果有什么问题?给出测试截图并分析原因。

task1_error

问题:第一行 Thomas 的解题数和罚时错误;缺少了 Jennie、Tibby、Vermont 的数据记录

原因:原文件 data_bad.txt 中 Thomas 的数据记录不完整,导致其直接读取了下一行记录的序号和学号;且 while 循环也被终止,无法继续读取

(2)思考:如何修改函数 std::vector<Contestant> load(const std::string& filename) ,使其提示出错行号并跳过有问题的数据行,同时让程序继续执行?

修改 load() 函数如下:

 1 // 从文件读取信息(跳过标题行)
 2 inline std::vector<Contestant> load(const std::string &filename)
 3 {
 4     std::ifstream is(filename);
 5     if (!is)
 6         throw std::runtime_error("fail to open " + filename);
 7 
 8     std::string line;
 9     std::getline(is, line); // 跳过标题
10 
11     std::vector<Contestant> v;
12     Contestant t;
13 
14     int seq;
15     int count = 2;
16 
17     while (std::getline(is, line))
18     {
19         std::istringstream iss(line);
20 
21         if (line.empty())
22         {
23             std::cerr << "[Warning] line " << count << " is empty, skipped." << std::endl;
24             continue;
25         }
26 
27         if (!line.empty())
28         {
29             if (!(iss >> seq) || !(iss >> t.id) || !(iss >> t.name) || !(iss >> t.major) || !(iss >> t.solved) || !(iss >> t.penalty))
30             {
31                 std::cerr << "[Warning] line " << count << " format error, skipped." << std::endl;
32                 continue;
33             }
34 
35             else
36                 v.push_back(t);
37         }
38 
39         count++;
40     }
41 
42     return v;
43 }

task1_repair

 

实验任务2

源代码task2

 1 // 学员类Student及其重载运算符函数声明
 2 
 3 #pragma once
 4 
 5 #include <iostream>
 6 #include <string>
 7 
 8 class Student
 9 {
10 public:
11     Student() = default;
12     ~Student() = default;
13 
14     const std::string get_major() const;
15     int get_grade() const;
16 
17     friend std::ostream &operator<<(std::ostream &os, const Student &s);
18     friend std::istream &operator>>(std::istream &is, Student &s);
19 
20 private:
21     int id;
22     std::string name;
23     std::string major;
24     int grade; // 0-100
25 };
student.hpp
 1 // 学员类Student及其重载运算符函数实现
 2 
 3 #include <string>
 4 #include <iomanip>
 5 
 6 #include "student.hpp"
 7 
 8 inline const std::string Student::get_major() const
 9 {
10     return major;
11 }
12 
13 inline int Student::get_grade() const
14 {
15     return grade;
16 }
17 
18 inline std::ostream &operator<<(std::ostream &os, const Student &s)
19 {
20     os << std::left;
21     os << std::setw(15) << s.id
22        << std::setw(15) << s.name
23        << std::setw(15) << s.major
24        << std::setw(15) << s.grade;
25 
26     return os;
27 }
28 
29 inline std::istream &operator>>(std::istream &is, Student &s)
30 {
31     is >> s.id >> s.name >> s.major >> s.grade;
32 
33     return is;
34 }
student.cpp
 1 // 学员成绩管理类StuMgr声明
 2 
 3 #pragma once
 4 
 5 #include <string>
 6 #include <vector>
 7 #include "student.hpp"
 8 
 9 class StuMgr
10 {
11 public:
12     void load(const std::string &file);       // 加载数据文件(空格分隔)
13     void sort();                              // 排序: 按专业字典序升序、同专业分数降序
14     void print() const;                       // 打印到屏幕
15     void save(const std::string &file) const; // 保存到文件
16 
17 private:
18     void write(std::ostream &os) const; // 把数据写到任意输出流
19 
20 private:
21     std::vector<Student> students;
22 };
stumgr.hpp
 1 // 学员成绩管理类stumgr实现
 2 
 3 #include <iostream>
 4 #include <fstream>
 5 #include <string>
 6 #include <algorithm>
 7 #include <stdexcept>
 8 
 9 #include "stumgr.hpp"
10 
11 inline bool compare(const Student &s1, const Student &s2)
12 {
13     if (s1.get_major() != s2.get_major())
14         return s1.get_major() < s2.get_major();
15 
16     return s1.get_grade() > s2.get_grade();
17 }
18 
19 inline void StuMgr::load(const std::string &file)
20 {
21     std::ifstream is(file);
22     if (!is)
23         throw std::runtime_error("cannot open file: " + file);
24 
25     std::string line;
26     std::getline(is, line);
27 
28     Student s;
29 
30     while (is >> s)
31     {
32         students.push_back(s);
33     }
34 }
35 
36 inline void StuMgr::sort()
37 {
38     if (students.size() == 0)
39         std::cerr << "(empty)" << std::endl;
40 
41     std::sort(students.begin(), students.end(), compare);
42 }
43 
44 inline void StuMgr::print() const
45 {
46     write(std::cout);
47 }
48 
49 inline void StuMgr::save(const std::string &file) const
50 {
51     std::ofstream os(file);
52     if (!os)
53         throw std::runtime_error("cannot open file: " + file);
54 
55     write(os);
56 }
57 
58 inline void StuMgr::write(std::ostream &os) const
59 {
60     for (const auto &i : students)
61         os << i << std::endl;
62 }
stumgr.cpp
 1 #include <iostream>
 2 #include <limits>
 3 #include <string>
 4 
 5 #include "student.hpp"
 6 #include "student.cpp"
 7 #include "stumgr.hpp"
 8 #include "stumgr.cpp"
 9 
10 const std::string in_file = "./data.txt";
11 const std::string out_file = "./ans.txt";
12 
13 void menu()
14 {
15     std::cout << "\n**********简易应用**********\n"
16                  "1. 加载文件\n"
17                  "2. 排序\n"
18                  "3. 打印到屏幕\n"
19                  "4. 保存到文件\n"
20                  "5. 退出\n"
21                  "请选择:";
22 }
23 
24 void app()
25 {
26     StuMgr mgr;
27 
28     while (true)
29     {
30         menu();
31         int choice;
32         std::cin >> choice;
33 
34         try
35         {
36             switch (choice)
37             {
38             case 1:
39                 mgr.load(in_file);
40                 std::cout << "加载成功\n";
41                 break;
42 
43             case 2:
44                 mgr.sort();
45                 std::cout << "排序已完成\n";
46                 break;
47 
48             case 3:
49                 mgr.print();
50                 std::cout << "打印已完成\n";
51                 break;
52 
53             case 4:
54                 mgr.save(out_file);
55                 std::cout << "导出成功\n";
56                 break;
57 
58             case 5:
59                 return;
60 
61             default:
62                 std::cout << "不合法输入\n";
63             }
64         }
65         catch (const std::exception &e)
66         {
67             std::cout << "Error: " << e.what() << '\n';
68         }
69     }
70 }
71 
72 int main()
73 {
74     app();
75 }
task2.cpp

运行测试截图

task2_1

task2_2

task2_3

task2_4

拓展

说明为解决数据完整性、有效性,提升代码健壮性,对哪些模块做了改动。

提供改动模块的源码及使用 data_bad.txt 运行测试效果截图。

  1 // 学员成绩管理类stumgr实现
  2 
  3 #include <iostream>
  4 #include <fstream>
  5 #include <string>
  6 #include <algorithm>
  7 #include <stdexcept>
  8 
  9 #include "stumgr.hpp"
 10 
 11 inline bool compare(const Student &s1, const Student &s2)
 12 {
 13     if (s1.get_major() != s2.get_major())
 14         return s1.get_major() < s2.get_major();
 15 
 16     return s1.get_grade() > s2.get_grade();
 17 }
 18 
 19 inline void StuMgr::load(const std::string &file)
 20 {
 21     std::ifstream is(file);
 22     if (!is)
 23         throw std::runtime_error("cannot open file: " + file);
 24 
 25     std::string line;
 26     std::getline(is, line);
 27 
 28     int line_num = 2;
 29 
 30     while (std::getline(is, line))
 31     {
 32         std::istringstream iss1(line);
 33 
 34         if (line.empty())
 35         {
 36             std::cerr << "[Warning] line " << line_num << " is empty, skipped." << std::endl;
 37 
 38             line_num++;
 39             continue;
 40         }
 41 
 42         else
 43         {
 44             int id, grade;
 45             std::string name, major;
 46 
 47             if (!(iss1 >> id >> name >> major >> grade))
 48             {
 49                 std::cerr << "[Warning] line " << line_num << " format error, skipped: ";
 50                 std::cout << line << std::endl;
 51 
 52                 line_num++;
 53                 continue;
 54             }
 55 
 56             else if (grade < 0 || grade > 100)
 57             {
 58                 std::cerr << "[Warning] line " << line_num << " grade invalid, skipped: ";
 59                 std::cout << line << std::endl;
 60 
 61                 line_num++;
 62                 continue;
 63             }
 64 
 65             else
 66             {
 67                 Student s;
 68                 std::istringstream iss2(line);
 69 
 70                 iss2 >> s;
 71 
 72                 students.push_back(s);
 73 
 74                 line_num++;
 75                 continue;
 76             }
 77         }
 78     }
 79 }
 80 
 81 inline void StuMgr::sort()
 82 {
 83     if (students.size() == 0)
 84         std::cerr << "(empty)" << std::endl;
 85 
 86     std::sort(students.begin(), students.end(), compare);
 87 }
 88 
 89 inline void StuMgr::print() const
 90 {
 91     write(std::cout);
 92 }
 93 
 94 inline void StuMgr::save(const std::string &file) const
 95 {
 96     std::ofstream os(file);
 97     if (!os)
 98         throw std::runtime_error("cannot open file: " + file);
 99 
100     write(os);
101 }
102 
103 inline void StuMgr::write(std::ostream &os) const
104 {
105     for (const auto &i : students)
106         os << i << std::endl;
107 }
stumgr.cpp

task2_5

task2_6

task2_7

posted @ 2025-12-17 21:33  FF-10086  阅读(0)  评论(0)    收藏  举报