C++对象与多态文件流程序:读取文件后显示崩溃问题求助
C++多态对象文件读写崩溃问题排查与解决
问题描述
基于Robert Lafore所著《C++面向对象编程》第12章(对象与多态的文件流)编写的程序,添加员工、显示员工、写入文件功能均正常,但关闭程序重新运行后,读取文件数据再执行显示操作时崩溃,报错为Process finished with exit code 139 (interrupted by signal 11: SIGSEGV),开发环境为Mac系统下的Clion。
崩溃原因
- 虚函数表指针无效:带有虚函数的类对象内存中会存储虚函数表指针(vptr),直接将对象二进制写入文件时,会把当前进程的虚函数表指针一并写入。重启程序后读取数据,该指针指向旧进程的无效内存地址,调用
gettype()、putdata()等虚函数时触发段错误。 - 对象内存布局不兼容:不同编译环境或同一环境的不同编译配置下,类对象的内存布局可能存在差异,直接二进制读写对象会导致数据解析错误,引发内存访问异常。
解决办法
需避免直接读写整个对象,改为序列化/反序列化对象的成员变量,具体修改步骤如下:
1. 修改write()函数:分类型写入成员变量
不再直接写入整个对象,而是逐个写入基类和派生类的成员数据:
void employee::write() { etype type; ofstream file("Data.DAT", ios::binary); for(int i=0;i<n;i++) { type = e[i]->gettype(); file.write(reinterpret_cast<char*>(&type), sizeof(etype)); switch(type) { case managerr: { manager* m = static_cast<manager*>(e[i]); // 写入基类成员 file.write(reinterpret_cast<char*>(m->name), 40); file.write(reinterpret_cast<char*>(&m->number), sizeof(int)); // 写入派生类成员 file.write(reinterpret_cast<char*>(m->title), 40); file.write(reinterpret_cast<char*>(&m->dues), sizeof(double)); break; } case scientistt: { scientist* s = static_cast<scientist*>(e[i]); file.write(reinterpret_cast<char*>(s->name), 40); file.write(reinterpret_cast<char*>(&s->number), sizeof(int)); file.write(reinterpret_cast<char*>(&s->pubs), sizeof(int)); break; } case laborerr: { laborer* l = static_cast<laborer*>(e[i]); file.write(reinterpret_cast<char*>(l->name), 40); file.write(reinterpret_cast<char*>(&l->number), sizeof(int)); break; } default: cout<<"Can't write type"<<endl; } } file.close(); }
2. 修改read()函数:分类型读取并赋值成员变量
创建新对象后,逐个读取文件中的成员数据并赋值:
void employee::read() { etype type; ifstream file("Data.DAT", ios::binary); while(true) { file.read(reinterpret_cast<char*>(&type), sizeof(etype)); if(file.eof()) break; switch(type) { case managerr: { manager* m = new manager; e[n] = m; // 读取基类成员 file.read(reinterpret_cast<char*>(m->name), 40); file.read(reinterpret_cast<char*>(&m->number), sizeof(int)); // 读取派生类成员 file.read(reinterpret_cast<char*>(m->title), 40); file.read(reinterpret_cast<char*>(&m->dues), sizeof(double)); break; } case scientistt: { scientist* s = new scientist; e[n] = s; file.read(reinterpret_cast<char*>(s->name), 40); file.read(reinterpret_cast<char*>(&s->number), sizeof(int)); file.read(reinterpret_cast<char*>(&s->pubs), sizeof(int)); break; } case laborerr: { laborer* l = new laborer; e[n] = l; file.read(reinterpret_cast<char*>(l->name), 40); file.read(reinterpret_cast<char*>(&l->number), sizeof(int)); break; } default: cout<<"Can't read type"<<endl; } n++; } }
3. 调整基类成员访问权限
为了让派生类能直接访问基类的name和number成员,将employee类的成员访问权限从private改为protected:
class employee { protected: char name[40]; int number; public: // 其余成员函数不变 };
内容的提问来源于stack exchange,提问作者Mohamed Helmy
相关产品推荐
相关产品推荐

