使用序列化C ++保存游戏状态

时间:2014-04-29 06:54:03

标签: c++ pointers serialization save

我有一个名为Game的课程,其中包含以下内容:

vector<shared_ptr<A>> attr; // attributes
D diff; // differences
vector<shared_ptr<C>> change; // change

我的问题是,如何将这些(保存)写入文件并稍后读取/加载? 我想过在其中使用struct,只是保存struct,但我不知道从哪里开始。

这是我到目前为止的尝试,只是试图保存change。我已经在这个问题上阅读了很多内容,而且我的问题(其中一个,无论如何)在这里似乎是我存储的指针在关闭程序后会无效(因为我也释放它们的事实)在退出之前)。

/* Saves state to file */
void Game::saveGame(string toFile) {
    ofstream ofs(toFile, ios::binary);
    ofs.write((char *)&this->change, sizeof(C));

    /* Free memory code here */
    ....

    exit(0);
}; 

/* Loads game state from file */
void Game::loadGame(string fromFile) {
    ifstream ifs(fromFile, ios::binary);
    ifs.read((char *)&this->change, sizeof(C));

    this->change.toString(); // display load results
};

任何人都可以指导我正确的方向来序列化这些数据吗?我只想使用标准套餐,所以没有boost

感谢。

3 个答案:

答案 0 :(得分:1)

我不知道如何实现类ACD,但这是第一个问题:如何序列化该类的对象。对于C案例,您需要实现以下内容:

std::ostream& operator <<(std::ostream& os, const C& c) {
    // ... code to serialize c to an output stream
    return os;
}
std::istream& operator >>(std::istream& is, C& c) {
    // ... code to populate c contents from the input stream
    return is;
}

或者,如果您愿意,可以为该课程创建write()read()函数。

好吧,如果你想序列化vector<shared_ptr<C>>看起来很明显,你不想序列化指针,而是内容。因此,您需要取消引用每个指针并进行序列化。如果在加载之前未知向量的大小(即,并不总是相同),则需要存储该信息。然后,您可以创建一对函数来序列化完整的向量:

std::ostream& operator <<(std::ostream& os, const std::vector<std::shared_ptr<C>>& vc) {
    // serialize the size of the vector using << operator
    // for each element of the vector, let it be called 'pc'
        os << *pc << std::endl; // store the element pointed by the pointer, not the pointer.
    return os;
}
std::istream& operator >>(std::istream& is, std::vector<std::shared_ptr<C>>& c) {
    // read the size of the vector using >> operator
    // set the size of the vector
    // for each i < sizeo of the vector, let 'auto &pc = vc[i]' be a reference to the i-th element of the vector
        C c;                         // temporary object
        is >> c;                     // read the object stored in the stream
        pc = std::make_shared<C>(c); // construct the shared pointer, assuming the class C has copy constructor

    return is;
}

然后,

/* Saves state to file */
void Game::saveGame(string toFile) {
    ofstream ofs(toFile);
    ofs << change;
    ....
}; 

/* Loads game state from file */
void Game::loadGame(string fromFile) {
    ifstream ifs(fromFile);
    ifs >> change;
};

我知道你还需要解决很多问题。我建议您进行调查以解决这些问题,以便您了解如何解决问题。

答案 1 :(得分:1)

您不仅要保存指针,还要尝试保存shared_ptr但使用错误的大小。

您需要为所有类编写序列化函数,注意不要只编写非POD类型的原始位。最安全的是始终为所有事物实施逐个成员的序列化,因为你永远不知道未来会带来什么 然后处理它们的集合只是存储有多少它的问题。

C的示例:

void Game::save(ofstream& stream, const C& data)
{
    // Save data as appropriate...
}

void Game::saveGame(string toFile) {
    ofstream ofs(toFile, ios::binary);

    ofs.write((char *)change.size(), sizeof(change.size());
    for (vector<shared_ptr<C>>::const_iterator c = change.begin(); c != change.end(); ++c)
    {
        save(ofs, **c);
    }
}; 


shared_ptr<C> Game::loadC(ofstream& stream)
{
    shared_ptr<C> data(new C);
    // load the object...
    return data;
}

void Game::loadGame(string fromFile) {
    change.clear();
    size_t count = 0;
    ifstream ifs(fromFile, ios::binary);
    ifs.read((char *)&count, sizeof(count));
    change.reserve(count);
    for (int i = 0; i < count; ++i)
    {
        change.push_back(loadC(ifs));
    }
};

当然缺少所有错误处理 - 您需要添加它。

至少从文本存储开始(使用<<>>)而不是二进制文件,这是一个好主意。当你可以在文本编辑器中编辑它时,更容易找到错误或者处理已保存的状态。

答案 2 :(得分:1)

编写自己的序列化是一项相当大的挑战。即使你不使用boost serializatoin,我也会建议你学习如何使用它并理解它是如何工作的,而不是自己发现它。

在序列化时,你最终会得到一个数据缓冲区,其内容你的想法很模糊。您必须保存能够恢复它所需的一切。你大块读了它。示例(未编译,未经测试且不时尚):

void save(ostream& out, const string& s)
{
   out << s.size();
   out.write(s.c_str(), s.size());
}
void load(istream& in, string& s)
{
   unsigned len;
   in >> len;
   s.resize(len);
   in.read((char*)s, len); 
}

struct Game
{
   void save(ostream& out)
   {
      player.save(out);
   };
   void load(istream& in)
   {
      player.load(in);
   }
};
struct Player
{
   void save(ostream& out)
   {
       // save in the same order as loading, serializing everything you need to read it back
       save(out, name);
       save(out, experience);
   }
   void load(istream& in)
   {
       load(in, name);
       load(in, experience); // 
   }
};

我不知道为什么你会这样做而不是使用提升,但这些是你应该考虑的一些情况: - 类型 - 你必须找到一种方法来了解什么&#34;变化类型&#34;你真的在那里。 - 一个字符串(矢量,无论如何) - 大小+数据(然后你从字符串读回的第一件事是长度,你调整它的大小并复制&#34;长度&#34;字符数) - 指针 - 保存指针指向的数据,然后在反序列化时必须分配它,构造它(通常是默认构造)并读回数据并将成员重置为它们各自的值。注意:您必须避免内存泄漏。 - 多态指针 - 你必须知道指针实际指向的是什么类型,你必须构造派生类型,保存派生类型的值...所以你必须保存类型信息 - 空指针...您必须区分空指针,以便您知道不需要进一步从流中读取数据。 - 版本控制 - 您必须能够在添加/删除字段后读取数据

你得到一个完整的答案太多了。