实验六
task1
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(15) << c.solved
<< std::setw(15) << 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"
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();
}


问题 1:
(1)std::cout 是标准输出流对象,std::ofstream 是文件输出流类,且 std::ofstream 继承自 std::ostream。C++ 中基类引用可以绑定到派生类对象,因此 write() 函数的 std::ostream& 参数能同时接受两者作为实参,实现了流操作的复用。
(2)不需要,只要新设备的输出流类继承自 std::ostream 并实现了 std::ostream 的接口,write() 函数可直接复用,无需修改代码,体现了面向对象的多态特性。
问题 2:
(1)save() 函数中,当文件打开失败时,抛出 std::runtime_error 异常,提示 “fail to open + 文件名”。load() 函数中,当文件打开失败时,抛出同样类型的异常。
(2)异常被 app() 函数中的 try-catch 块捕获。通过 e.what() 输出异常信息到标准错误流 std::cerr,并返回,终止程序后续执行。
问题3:
可以,功能、性能、结果一致。
问题 4:
问题:程序可能崩溃、输出不完整或出现乱码。
原因:data_bad.txt 包含空白行、字段缺失、非法值、格式错误。原 load() 函数通过 is >> seq >> t 读取数据,当遇到非法格式时,输入流会进入错误状态,后续读取全部失败,导致数据读取不完整;若非法值无法转换为对应类型,会引发未定义行为。

task2
student.hpp
#pragma once
#include<iostream>
#include<string>
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;
};
student.cpp
#include<iostream>
#include<string>
#include<iomanip>
#include "student.hpp"
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 << std::left;
os << std::setw(15) << s.id
<< std::setw(15) << s.name
<< std::setw(15) << s.major
<< std::setw(15) << s.grade;
return os;
}
std::istream& operator>>(std::istream& is,Student& s){
is >> s.id >> s.name >> s.major >> s.grade;
return is;
}
stumgr.hpp
#pragma once
#include<string>
#include<vector>
#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;
};
stumgr.cpp
#include "stumgr.hpp"
#include <fstream>
#include <stdexcept>
#include <algorithm>
#include <sstream>
#include <<iomanip>
void StuMgr::load(const std::string& file) {
std::ifstream is(file);
if (!is) {
throw std::runtime_error("cannot open file: " + file);
}
students.clear();
Student s;
int line_num = 1;
std::string line;
while (std::getline(is, line)) {
if (line.empty()) continue;
std::istringstream iss(line);
if (iss >> s) {
students.push_back(s);
} else {
std::cerr << "[Warning] Line " << line_num << ": format error or invalid grade, skipped\n";
}
line_num++;
}
is.close();
}
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 os(file);
if (!os) {
throw std::runtime_error("cannot open file: " + file);
}
write(os);
os.close();
}
void StuMgr::write(std::ostream& os) const {
for (const auto& stu : students) {
os << stu << '\n';
}
}
task2.cpp
#include<iostream>
#include<limits>
#include<string>
#include "stumgr.hpp"
const std::string in_file="./data.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();
}



实验总结
掌握了 C++ 流类库的使用,iostream 实现控制台 I/O,fstream 实现文件 I/O,通过基类引用 std::ostream& 实现流操作复用。理解并实践了异常处理机制,throw 抛出异常、try-catch 捕获处理,确保文件操作等可能出错的场景有友好提示。深化了面向对象编程应用:封装、继承、多态、lambda 表达式与排序算法结合。提升了代码健壮性意识,通过数据校验、错误处理、按行读取等方式,处理空白行、字段缺失、非法值等异常情况。

浙公网安备 33010602011771号