嗨,我目前正在开发一个模拟程序,该程序尝试在请求时将程序的状态(变量和对象)保存到二进制文件中,以便在需要时可以恢复模拟。
请注意:我知道这在不同的 CPU 架构之间不兼容,这绝对没问题!
一切似乎都运行良好,直到将具有虚拟方法的对象写入文件,然后尝试将其读回。
下面的代码说明了这个问题:
header.hpp
using namespace std;
class parent
{
public:
int mValue;
virtual string getName() =0;
virtual size_t getSize() =0;
parent(int value) : mValue(value)
{
}
};
class bob : public parent
{
public:
bob(int value) : parent(value)
{
}
string getName();
size_t getSize() { return sizeof(bob); }
};
string bob::getName()
{
string name("bob");
return name;
}
class sarah : public parent
{
public:
sarah(int value) : parent(value)
{
}
string getName();
size_t getSize() { return sizeof(sarah); }
};
string sarah::getName()
{
string name("sarah");
return name;
}
write.cpp
#include <iostream>
#include <fstream>
#include <string>
#include "header.hpp"
int main()
{
sarah girl(1);
bob boy(2);
parent* child1 = &girl;
parent* child2 = &boy;
cout << "Created child called " << child1->getName() << endl;
cout << "Created child called " << child2->getName() << endl;
//save sarah and bob to a binary file
ofstream file("temp.bin", ios::binary | ios::trunc);
if(!file.is_open())
return 1;
//format <size><data><size><data>....
size_t tempSize=0;
//write child1
tempSize = child1->getSize();
file.write( (char*) &tempSize,sizeof(size_t));
file.write( (char*) child1,tempSize);
tempSize = child2->getSize();
file.write( (char*) &tempSize,sizeof(size_t));
file.write( (char*) child2,tempSize);
file.close();
return 0;
}
read.cpp
#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
#include "header.hpp"
int main()
{
//read sarah and bob from a binary file
ifstream file("temp.bin", ios::binary);
//format <size><data><size><data>....
size_t tempSize=0;
//get size of child1
file.read( (char*) &tempSize, sizeof(size_t));
//allocate memory for child1
parent* child1= (parent*) malloc(tempSize);
//read child 1 back
file.read( (char*) child1,tempSize);
//get size of child2
file.read( (char*) &tempSize, sizeof(size_t));
//allocate memory for child2
parent* child2= (parent*) malloc(tempSize);
//read child 2 back
file.read( (char*) child2,tempSize);
file.close();
//Using virtual methods causes SEGFAULT
cout << "Recreated child" << child1->getName() << endl;
cout << "Recreated child" << child2->getName() << endl;
return 0;
}
并按如下方式构建和运行:
g++ -g write.cpp -o write ; ./write
g++ -g read.cpp -o read ; ./read
当我逐步执行 gdb 中的读取程序时,我注意到问题似乎出在 v-table 指针上。当我在读取程序中重新创建“sarah”(child1)时,v-table 指针是存在于写入程序中的指针,而不是读取程序。所以大概这个写程序中“sarah”的v-table指针指向一个无效的内存区域,这导致了SEGFAULT。
我有两个问题:
我想出了一种确定要调用哪个构造函数的方法。这将是
这不是很优雅,因为每次我添加一个派生自“父”的新类时,我都必须给它一个 ID 并将它添加到“读取”中的 switch 语句中。有更好的方法吗?
感谢阅读,我知道我的帖子很长!
最佳答案
每次编译程序时,它都会将函数放在内存中的不同位置。此外,在某些操作系统配置中,每次重新启动程序时,函数甚至可能会移动。这是一种称为地址空间布局随机化的安全功能。如果您确定将从完全相同的二进制文件中读取和写入对象,那么您可以通过将读取和写入函数放在同一个程序中而不是两个不同的程序中来做您想做的事情。然而,即使这样也充满了问题,如果您进行更改并重新编译,您将无法再读取旧数据文件。Boost::Serialization
是专门为避免所有这些问题而创建的,包括我确定一些我什至不知道的问题,经过严格的同行评审和测试,并且有一个非常自由的许可证作为奖励。使用这样的图书馆不是“诉诸”的事情,而是一种特权。