二进制文件存储含map<string,int>的student类对象报错求助
解决包含
std::map<string, int>的类对象二进制读写错误问题 问题根源
你直接用ofstream::write和ifstream::read读写student类对象的方式之所以出错,核心原因是:
std::map<string, int>和std::string属于非POD(Plain Old Data)类型,它们的内部结构包含指向堆内存的指针(比如std::string存储的是字符数组的地址,std::map是树形节点的指针)。- 直接写入对象内存时,只会保存这些指针的地址值,而非指针指向的实际数据;读取时,这些地址在当前进程中是无效的,访问就会触发未定义行为(崩溃、乱码等)。
- 换成字符数组能正常工作,是因为字符数组是POD类型,内存中存储的就是实际数据,没有堆指针依赖。
解决方案:手动序列化/反序列化
要正确读写包含非POD类型的类对象,需要手动实现序列化(将对象数据转换为可存储的二进制格式)和反序列化(从二进制数据还原对象)逻辑,把复杂类型拆解为基础数据逐个处理。
修改后的完整代码
#include <iostream> #include <fstream> #include <map> #include <cstring> #include <string> using namespace std; class student { public: int rollno; char name[50]; int sub[10]; map<string, int> marks; // 序列化:将对象数据写入文件流 void serialize(ofstream& ofs) const { // 直接写入POD类型成员 ofs.write(reinterpret_cast<const char*>(&rollno), sizeof(rollno)); ofs.write(name, sizeof(name)); ofs.write(reinterpret_cast<const char*>(sub), sizeof(sub)); // 先写入map的元素数量,方便读取时循环 size_t map_size = marks.size(); ofs.write(reinterpret_cast<const char*>(&map_size), sizeof(map_size)); // 逐个写入map的键值对 for (const auto& kv : marks) { // 写入string的长度,再写入字符内容 size_t str_len = kv.first.size(); ofs.write(reinterpret_cast<const char*>(&str_len), sizeof(str_len)); ofs.write(kv.first.c_str(), str_len); // 写入对应的分数 ofs.write(reinterpret_cast<const char*>(&kv.second), sizeof(kv.second)); } } // 反序列化:从文件流读取数据还原对象 void deserialize(ifstream& ifs) { // 读取POD类型成员 ifs.read(reinterpret_cast<char*>(&rollno), sizeof(rollno)); ifs.read(name, sizeof(name)); ifs.read(reinterpret_cast<char*>(&sub), sizeof(sub)); // 读取map的元素数量 size_t map_size; ifs.read(reinterpret_cast<char*>(&map_size), sizeof(map_size)); // 清空原有数据,避免残留 marks.clear(); for (size_t i = 0; i < map_size; ++i) { // 读取string的长度和内容 size_t str_len; ifs.read(reinterpret_cast<char*>(&str_len), sizeof(str_len)); string subject(str_len, '\0'); ifs.read(&subject[0], str_len); // 读取分数 int score; ifs.read(reinterpret_cast<char*>(&score), sizeof(score)); marks.emplace(subject, score); } } }; int main() { int totalsub; student st; st.rollno = 21; strcpy(st.name, "Aman Tiwari"); cout << "enter total subject :"; cin >> totalsub; for (int i = 0; i < totalsub; i++) { int num; string str; cin.ignore(); cout << "enter " << i << " subject name :"; getline(cin, str); cout << "enter marks:"; cin >> num; st.marks.insert(pair<string, int>(str, num)); } // 序列化写入文件 ofstream ofobj("test.dat", ios::binary | ios::trunc); st.serialize(ofobj); ofobj.close(); // 反序列化读取文件 ifstream ifobj("test.dat", ios::binary); student temp; temp.deserialize(ifobj); ifobj.close(); cout << " the data is " << endl; cout << temp.rollno << endl; cout << temp.name << endl; for (auto &mark : temp.marks) { cout << mark.first << " " << mark.second << endl; } cout << "After showing !!"; return 0; }
代码说明
- POD成员处理:
rollno、name、sub都是POD类型,可直接用write/read读写内存。 - std::map处理:先写入元素数量,再逐个处理每个键值对,确保读取时知道要循环多少次。
- std::string处理:先写入字符串的长度,再写入字符内容,避免读取时无法确定字符串边界。
- 文件打开模式:写入时用
ios::trunc覆盖旧文件(如果需要追加可以保留ios::app,但反序列化时要注意处理多个对象)。
其他可选方案
- 如果可以使用第三方库,Boost.Serialization提供了更便捷的序列化机制,无需手动编写每个成员的读写逻辑。
- C++20及以上可考虑使用
std::format配合文本存储(非二进制),但二进制存储效率更高。
内容的提问来源于stack exchange,提问作者Aman
相关产品推荐
相关产品推荐

