2011-02-26 61 views
3

嗨我目前正在研究一个模拟程序,它试图将程序的状态(变量和对象)保存到二进制文件中,以便它可以恢复如果需要模拟。使用虚拟方法将对象写入和读取到二进制文件

就像一个笔记:我知道这是不同的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表指针。当我在读程序中重新创建“sarah”(child1)时,v表指针是写程序存在的指针,而不是读程序。所以大概这个写程序中这个“sarah”的v表指针指向一个导致SEGFAULT的无效内存区域。

我有两个问题:

  • 是否有可能在V表指针信息保存到二进制文件中的“写”方案,让我的对象是在“正确”的程序完全重建而不诉诸像Boost :: Serialization或POST ++这样的库来为我处理这个问题?

  • 如果这是不可能的,或者它非常复杂,那么我将不得不添加一个构造函数和一个“saveState()”方法(它们可以分别作用于ifstream和ofstream对象),以便每个类(在这种情况下sarah和bob)处理保存并从二进制文件读取它的状态。问题在于我有多个派生自类“parent”的类,所以我需要一种方法让“读取”程序计算出读取二进制文件时调用哪个构造函数。

    我想出了一种方法来确定调用哪个构造函数。这将是

  • 给予每一个从“父”的唯一ID

  • 在“写”程序得出的二进制文件
  • 添加唯一的ID在“读”程序读取每个唯一的类ID,然后使用switch语句调用相关的构造函数。

    这不是很优雅,但每当我添加一个派生自“父”的新类时,我必须给它一个ID并将它添加到“读”中的switch语句中。有没有更好的方法来做到这一点?

感谢您的阅读,我知道我的帖子很长!

回答

1

每次你的程序被编译时,它都会将函数放在内存中的不同位置。另外,在某些操作系统配置中,每次重新启动程序时,函数甚至可能会移动。这是一种称为地址空间布局随机化的安全功能。如果您知道肯定您将会从完全相同的二进制文件读取和写入对象,您可以通过将您的读取和写入功能放入同一个程序而不是两个不同的二进制文件来执行您想要的操作。但是,即使这样也存在这样的问题,即如果您进行更改并重新编译,则不再能够读取旧的数据文件。

Boost::Serialization是专门为避免所有这些问题而创建的,其中包括我相信我甚至没有意识到的一些问题,经过同行评审和测试,并且拥有极其宽松的许可证作为奖励。使用这样的图书馆不是“被诉诸”的东西,这是一种特权。