当前位置: 首页 > news >正文

OOP-实验6

实验任务1

源代码 contestant.hpp,utils.hpp,task1.cpp

点击查看代码 contestant.hpp
#pragma once
#include <iomanip>
#include <iostream>
#include <string>struct Contestant
{long id;           // 学号std::string name;  // 姓名std::string major; // 专业int solved;        // 解题数int penalty;       // 总罚时
};// 重载<<
// 要求:姓名/专业里不含空白符
inline std::ostream &operator<<(std::ostream &out, const Contestant &c)
{out << std::left;out << std::setw(15) << c.id<< std::setw(15) << c.name<< std::setw(15) << c.major<< std::setw(10) << c.solved<< std::setw(10) << c.penalty;return out;
}// 重载>>
inline std::istream &operator>>(std::istream &in, Contestant &c)
{in >> c.id >> c.name >> c.major >> c.solved >> c.penalty;return in;
}
点击查看代码 utils.hpp
#pragma once
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
#include "contestant.hpp"// ACM 排序规则:先按解题数降序,再按罚时升序
inline bool cmp_by_solve(const Contestant &a, const Contestant &b)
{if (a.solved != b.solved)return a.solved > b.solved;return a.penalty < b.penalty;
}// 将结果写至任意输出流
inline void write(std::ostream &os, const std::vector<Contestant> &v)
{for (const auto &x : v)os << x << '\n';
}// 将结果打印到屏幕
inline void print(const std::vector<Contestant> &v)
{write(std::cout, v);
}// 将结果保存到文件
inline void save(const std::string &filename, const std::vector<Contestant> &v)
{std::ofstream os(filename);if (!os)throw std::runtime_error("fail to open " + filename);write(os, v);
}// 从文件读取信息(跳过标题行)
inline std::vector<Contestant> load(const std::string &filename)
{std::ifstream is(filename);if (!is)throw std::runtime_error("fail to open " + filename);std::string line;std::getline(is, line); // 跳过标题std::vector<Contestant> v;Contestant t;int seq;while (is >> seq >> t)v.push_back(t);return v;
}
点击查看代码 task1.cpp
#include <algorithm>
#include <iostream>
#include <stdexcept>
#include <vector>
#include "contestant.hpp"
#include "utils.hpp"const std::string in_file = "./data.txt";
const std::string out_file = "./ans.txt";void app()
{std::vector<Contestant> contestants;try{contestants = load(in_file);std::sort(contestants.begin(), contestants.end(), cmp_by_solve);print(contestants);save(out_file, contestants);}catch (const std::exception &e){std::cerr << e.what() << '\n';return;}
}int main()
{app();
}

运行测试截图

img

img

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

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

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

  • 回答:std::coutstd::ostream对象,而std::ofstream继承自std::ostream

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

  • 回答:无需改动write()

  • 问题2:异常处理与捕获

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

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

  • 回答:未成功打开文件时。

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

  • 回答:被task1.cppapp()捕获,输出错误信息。

  • 问题3:替代写法

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

[](const Contestant &a, const Contestant &b)
{ return a.solved != b.solved ? a.solved > b.solved: a.penalty < b.penalty; }
  • 回答:可以,功能和结果与先前之写法一致,由于此lambda表达式较为简洁,被定义为内联函数,性能更优。

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

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

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

  • 回答:测试截图如下所示。由于第七个参赛选手的solvedpenalty是空的,导致错误读取到下一个参赛选手的序号和id;当下一个参赛选手读取其id时,遇到std::string类型,读取结束,故后三个参赛选手数据缺失。

img

img

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

  • 回答:改动之处如下所示。

// 在util.hpp的inline std::vector<Contestant> load(const std::string &filename)之中// 替换前的代码//  while (is >> seq >> t)//      v.push_back(t);// 替换后的代码while (true){if (is.eof())break;std::string line;std::getline(is, line);std::istringstream iss(line);try{iss >> seq >> t;}catch (const std::exception &e){std::ostringstream oss;oss << "line " << seq << " " << e.what();std::cerr << oss.str() << std ::endl;continue;}v.push_back(t);}// 在contestant中// 替换前的代码
// inline std::istream &operator>>(std::istream &in, Contestant &c)
// {
//     in >> c.id >> c.name >> c.major >> c.solved >> c.penalty;//     return in;
// }
// 替换后的代码
inline std::istream &operator>>(std::istream &in, Contestant &c)
{if (!(in >> c.id >> c.name >> c.major >> c.solved >> c.penalty))throw std::runtime_error("format error");return in;
}

实验任务2

源代码 student.hpp,stumgr.hpp,task2.cpp

点击查看代码 student.hpp
#pragma once#include <iostream>
#include <string>
#include <sstream>class Student
{
public:Student() = default;~Student() = default;const std::string get_major() const;int get_grade() const;friend std::ostream &operator<<(std::ostream &os, const Student &s);friend std::istream &operator>>(std::istream &is, Student &s);private:int id;std::string name;std::string major;int grade; // 0-100
};const std::string Student::get_major() const
{return major;
}int Student::get_grade() const
{return grade;
}std::ostream &operator<<(std::ostream &os, const Student &s)
{os << s.id << '\t' << s.name << '\t' << s.major << '\t' << s.grade;return os;
}std::istream &operator>>(std::istream &is, Student &s)
{std::string line;std::getline(is, line);std::istringstream iss(line);std::ostringstream oss;if (!(iss >> s.id)){oss.str("");oss << "format error,skipped: ";throw std::runtime_error(oss.str());}else if (!(iss >> s.name)){oss.str("");oss << "format error,skipped: " << s.id;throw std::runtime_error(oss.str());}else if (!(iss >> s.major)){oss.str("");oss << "format error,skipped: " << s.id << '\t' << s.name;throw std::runtime_error(oss.str());}else if (!(iss >> s.grade)){oss.str("");oss << "format error,skipped: " << s.id << '\t' << s.name << '\t' << s.major;throw std::runtime_error(oss.str());}else if (s.grade < 0 || s.grade > 100){std::cerr << "error: " << s.grade << std::endl;oss.str("");oss << "grade invalid,skipped: " << s.id << '\t' << s.name << '\t' << s.major << '\t' << s.grade;throw std::runtime_error(oss.str());}return is;
}
点击查看代码 stumgr.hpp
#pragma once
#include <string>
#include <vector>
#include <fstream>
#include <algorithm>
#include "student.hpp"class StuMgr
{
public:void load(const std::string &file);       // 加载数据文件(空格分隔)void sort();                              // 排序: 按专业字典序升序、同专业分数降序void print() const;                       // 打印到屏幕void save(const std::string &file) const; // 保存到文件private:void write(std::ostream &os) const; // 把数据写到任意输出流private:std::vector<Student> students;
};void StuMgr::load(const std::string &file)
{students.clear();std::ifstream ifs(file);if (!ifs){throw std::runtime_error("cannot open file: " + file);}std::string header;std::getline(ifs, header);Student s;int row = 1;while (true){try{ifs >> s;if (ifs.eof())break;++row;students.push_back(s);}catch (const std::exception &e){++row;std::cerr << "[Warning] line " << row << " " << e.what() << '\n';}}
}void StuMgr::sort()
{std::sort(students.begin(), students.end(), [](const Student &a, const Student &b){ if (a.get_major() != b.get_major())return a.get_major() < b.get_major();return a.get_grade() > b.get_grade(); });
}void StuMgr::print() const
{write(std::cout);
}void StuMgr::save(const std::string &file) const
{std::ofstream ofs(file);if (!ofs){throw std::runtime_error("无法打开文件: " + file);}write(ofs);
}void StuMgr::write(std::ostream &os) const
{for (const auto &s : students){os << s << std::endl;}
}
点击查看代码 task2.cpp
#include <iostream>
#include <limits>
#include <string>
#include "stumgr.hpp"const std::string in_file = "./data_bad.txt";
const std::string out_file = "./ans.txt";void menu()
{std::cout << "\n**********简易应用**********\n""1. 加载文件\n""2. 排序\n""3. 打印到屏幕\n""4. 保存到文件\n""5. 退出\n""请选择:";
}void app()
{StuMgr mgr;while (true){menu();int choice;std::cin >> choice;try{switch (choice){case 1:mgr.load(in_file);std::cout << "加载成功\n";break;case 2:mgr.sort();std::cout << "排序已完成\n";break;case 3:mgr.print();std::cout << "打印已完成\n";break;case 4:mgr.save(out_file);std::cout << "导出成功\n";break;case 5:return;default:std::cout << "不合法输入\n";}}catch (const std::exception &e){std::cout << "Error: " << e.what() << '\n';}}
}int main()
{app();
}

运行测试截图

无法打开文件

img

data.txt

img

img

data_bad.txt

img

img

实验总结

  • 对输入的student数据进行异常判断时,原先以依次读取各个基本数据的方式,但此方式可能会错误读取到下一行数据,故采用先读取一行,再对一行内的数据进行处理。

  • 读入student的每个数据项后,均会判断是否正确读入,虽然代码显得有些繁琐,但是可以确认从哪个数据项被中断,并在错误信息中输出已正确读入的数据项。

  • 异常处理时输出的行号,实际上是包含表头的,即表头算作第1行。

http://www.rkmt.cn/news/114613.html

相关文章:

  • 医疗和教育行业自动化、精准匹配、易掌握的数据分类分级最佳实践与案例
  • 毕设 深度学习yolo藻类细胞检测识别(科研辅助系统)(源码+论文)
  • RAG知识库构建策略
  • C++中的共用体与枚举:内存优化与类型安全
  • 2025年下半年鄂尔多斯车牌识别供应商推荐榜单 - 2025年品牌推荐榜
  • 2025年12月单股加固型网带,双股加固型网带,链式网带厂家品牌推荐榜,彰显国产技术实力 - 品牌鉴赏师
  • 【高可用架构必备技能】:Docker Offload中任务状态同步的7种最佳实践
  • 【Docker-LangGraph Agent配置终极指南】:掌握高效AI代理部署的5大核心技巧
  • HCA解码器完整教程:快速转换游戏音频的终极方案
  • 从零构建智能监控体系,基于Agent的Docker告警实战详解
  • Mem Reduct终极内存优化:三步让老电脑重获新生
  • Mem Reduct内存管理工具:系统性能优化实战指南
  • 15、网页数据处理与自动化操作实用指南
  • JRebel 激活失效?手把手教你本地搭建激活服务器(无需公网、无需 Docker)
  • 终极自适应解决方案:autofit.js一键实现完美大屏适配
  • 【读书笔记】《孙子兵法》
  • 谷歌关停暗网监控工具:2026年安全防护迎来“精准化”转型
  • 18、利用 SSH 实现安全的远程访问
  • Pearcleaner Homebrew管理:3步告别复杂命令行操作
  • 国产算力崛起背景下,大模型训练数据集的 “采洗之道”:技术实践与效率优化
  • 有源逻辑探头的具体应用
  • 高并发下,TPS/QPS/并发数这三者的区别?
  • 基于WPF的半导体设备配方管理程序技术方案
  • 半导体行业ALD阀技术路线分析及解决方案教程
  • Delphi中循环删除记录的实现方法
  • 16、远程系统管理与安全设置全攻略
  • 【Linux网络】传输层协议UDP - 详解
  • 17、系统安全、文本编辑与特殊字符变量全解析
  • 27、Linux系统初始化管理:从System V init到systemd
  • 22、软件更新机制全解析:从理论到实践