使用vector时无法读取文件中的C++类对象,求解决方法
兄弟,我看你在VS2017 Community里写C++代码的时候,遇到了读取文件里的Company类对象就抛异常的问题,我来帮你一步步分析解决!
先回顾下你的场景:你写了一个程序,支持写入类对象到二进制文件Ma.bin,或者从文件读取指定对象显示。但选择读取(选项1)时,执行File.read((char *)&Temp, sizeof(Temp))这行代码就会抛出异常,注释掉这行程序就能正常跑,对吧?
问题根源分析
我看了你的代码,发现几个关键问题,正是这些问题导致了异常:
变量作用域错误
你在try块里声明了vector<Company> Object;,但catch块的作用域和try是相互独立的,所以catch里的Object.clear();、Object.push_back(Temp);这些代码其实都是非法的!编译器应该已经给你标红警告了吧?这种未定义行为很容易导致内存访问错误,直接触发异常。类对象的二进制读写前提不满足
直接用File.write/read配合sizeof(Company)读写类对象,要求Company必须是POD类型(Plain Old Data)。如果你的Company类里包含这些内容,绝对不能这么干:std::string、std::vector这类标准容器- 动态分配内存的指针(比如
char* companyName) - 虚函数(类里有虚函数就会有虚表指针,内存拷贝会破坏虚表)
- 其他非POD类型的成员变量
文件操作的错误处理缺失
你没有检查文件是否成功打开,比如如果Ma.bin不存在,File.open(...)会失败,后面的seekg、read操作都会直接出错。另外,计算对象总数的File.tellg() / sizeof(Company)也有问题:如果文件大小不是sizeof(Company)的整数倍,结果会出错,而且streampos和size_t的类型转换也可能导致意外。输入合法性未校验
你读取用户输入的对象编号时,没有检查编号是否在1到total之间,如果用户输入一个超出范围的数,会导致文件读取位置越界,直接触发异常。
一步步解决方法
1. 修正变量作用域
把vector<Company> Object;和fstream File;移到try块外面,让catch块能正常访问这些变量:
int main() { fstream File; Company Temp; vector<Company> Object; // 移到try块外,扩大作用域 try{ cout << " 1. Show Data "; cout << " 2. Put Data\n"; cout << " => "; int a; cin >> a; cin.ignore(1000, '\n'); if (a == 1) throw 5; Temp.Get_Data(); Object.push_back(Temp); File.open("Ma.bin", ios::binary | ios::out | ios::app); if (!File.is_open()) { cout << "写入时打开文件失败!" << endl; return 1; } for (size_t i = 0; i < Object.size(); ++i) { File.write((char *)&Object[i], sizeof(Object[i])); } File.close(); } catch (int) { Object.clear(); File.open("Ma.bin", ios::binary | ios::in); if (!File.is_open()) { cout << "读取时打开文件失败!" << endl; return 1; } File.seekg(0, ios::end); streampos fileSize = File.tellg(); if (fileSize % sizeof(Company) != 0) { cout << "文件损坏或格式不正确!" << endl; File.close(); return 1; } auto total = fileSize / sizeof(Company); cout << " total Object write " << total << "\n"; cout << " Which One you want to Display ?"; int Number; cin >> Number; cin.ignore(1000, '\n'); if (Number < 1 || Number > total) { cout << "无效的编号,请输入1到" << total << "之间的数!" << endl; File.close(); return 1; } int Position = (Number - 1) * sizeof(Company); File.seekg(Position); File.read((char *)&Temp, sizeof(Temp)); Object.push_back(Temp); File.close(); auto It = Object.begin(); It->Put_Data(); } return 0; }
2. 确保Company类是合法的POD类型(或实现序列化函数)
如果你的Company类包含非POD成员(比如std::string),那必须自己实现序列化和反序列化函数,不能直接内存拷贝。举个例子:
#include <string> #include <fstream> #include <iostream> using namespace std; class Company { private: std::string name; int companyId; public: void Get_Data() { cout << "输入公司名称:"; getline(cin, name); cout << "输入公司ID:"; cin >> companyId; cin.ignore(1000, '\n'); } void Put_Data() const { cout << "公司名称:" << name << endl; cout << "公司ID:" << companyId << endl; } // 序列化到文件 void writeToFile(std::fstream& file) const { // 先写字符串长度,再写字符串内容 size_t nameLen = name.size(); file.write(reinterpret_cast<const char*>(&nameLen), sizeof(nameLen)); file.write(name.c_str(), nameLen); // 写入int类型的ID file.write(reinterpret_cast<const char*>(&companyId), sizeof(companyId)); } // 从文件反序列化 void readFromFile(std::fstream& file) { size_t nameLen; file.read(reinterpret_cast<char*>(&nameLen), sizeof(nameLen)); name.resize(nameLen); file.read(&name[0], nameLen); file.read(reinterpret_cast<char*>(&companyId), sizeof(companyId)); } };
然后读写的时候就调用这些函数,替换原来的write/read:
// 写入时 for (const auto& comp : Object) { comp.writeToFile(File); } // 读取时 Temp.readFromFile(File);
3. 补充错误处理
上面的修正代码里已经加了文件打开检查、文件格式检查、输入合法性检查,这些能避免很多意外的异常。
异常截图
你提到的异常截图如下:
内容的提问来源于stack exchange,提问作者user9301263

