c ++使用结构中的指针保存和加载游戏

时间:2010-11-30 13:32:27

标签: c++ file pointers struct save

我知道我可以使用:

MyGame  game; // the game object
//

ofstream out("mygame.bin", ios::binary);
out.write((char *)&game, sizeof(MyGame));

保存并加载游戏,但如果我在MyGame结构中有指针怎么办?指针只会被保存但不会指向它指向的数据吗?

和:如何解决这个问题?

9 个答案:

答案 0 :(得分:5)

你不能只是编写一个指向流的指针,并希望它能够神奇地完成。您需要在对象中实现保存/加载方法。 E.g:

class Serializable
{
    virtual void save(std::ofstream& _out) const = 0;
    virtual void load(std::ifstream& _in) = 0;
}; // eo class Serializable


// some game object
class MyObject : public Serializable
{
    int myInt;
    std::string myString;

    virtual void save(std::ofstream& _out) const
    {
        _out << myInt << myString;
    }; // eo save

    virtual void load(std::ifstream& _in)
    {
        _in >> myInt >> myString;
    }; // eo load
}; // eo class SomeObject

class MyGame : public Serializable
{
    MyObject a;
    MyObject b;

    virtual void save(std::ofstream& _out) const
    {
        a.save(_out);
        b.save(_out);
    };  // eo save

    virtual void load(std::ifstream& _in)
    {
        a.load(_in);
        b.load(_in);
    };  // eo load
}; // eo class MyGame

答案 1 :(得分:2)

假设你没有重写char * cast,是的,这很可能只保存指针而不是数据。

您需要的是对象的序列化。您可以提供一种方法来编组比特流中的对象状态并将其写出来。而且你还需要有恢复状态的方法。

您可以在wikipedia

上阅读有关序列化的更多信息

答案 2 :(得分:1)

您可以重载流出运算符(<<)并流出每个字段(反之亦然)

编辑:这是一个完整的例子......

#include <iostream>
#include <fstream>
#include <map>

using namespace std;

template <typename T>
void serialize(ostream& str, const T& field)
{
  str.rdbuf()->sputn(reinterpret_cast<const char*>(&field), sizeof(T));
}

template <typename T>
void deserialize(istream& str, T& field)
{
  str.rdbuf()->sgetn(reinterpret_cast<char*>(&field), sizeof(T));
}

class MyGame
{
public:
 MyGame() : a(), b() {}
 MyGame(int av, int bv) : a(av), b(bv) {}

 friend ostream& operator<<(ostream& str, MyGame const& game);
 friend istream& operator>>(istream& str, MyGame& game);

  int getA() const { return a; }
  int getB() const { return b; }

private:
 int a;
 int b;
};

ostream& operator<<(ostream& str, MyGame const& game)
{
  serialize(str, game.a);
  serialize(str, game.b);
  return str;
}

istream& operator>>(istream& str, MyGame& game)
{
  deserialize(str, game.a);
  deserialize(str, game.b);
  return str;
}

int main(void)
{
  {
    ofstream fout("test.bin", ios::binary);
    MyGame game(10, 11);
    fout << game;
  }

  {
    ifstream fin("test.bin", ios::binary);
    MyGame game;
    fin >> game;
    cout << "game.a: " << game.getA() << ", game.b: " << game.getB() << endl;
  }

  return 0;
}

您必须了解此方法的问题,例如生成的文件将是特定于平台的(即非便携式)等。

答案 3 :(得分:1)

Boost有一个serialization library,内置支持深度指针保存和恢复,以及对共享数据指针的正确序列化。

这是一个相当广泛的库,但您不需要编写那么多代码就可以开始在自己的项目中使用它。除了我认为最简单的序列化要求外,非常值得学习。

答案 4 :(得分:0)

试试game.serialize(out);。在serialize成员函数中调用指针成员的序列化。

答案 5 :(得分:0)

为每个需要持久的类型创建一个序列化函数 为每个成员打电话。

它实际上类似于通过网络进行序列化或可视化以实现调试目的。

boost.serialize可以帮到你。

答案 6 :(得分:0)

只是转储指针值的“Naive”序列化永远不会起作用,因为在反序列化时,这些指针将无效。

解决这类问题的一般方法是这样的:

  1. 让游戏中可以序列化的每个对象实现一个serialize()虚函数(我假设所有这些对象最终都来自同一个基类)。
  2. 让基类实现get_serialized_id()公共函数。此函数将使用静态自动递增变量为每个对象实例生成唯一ID,但仅在第一次调用它时(后续调用将仅返回现有值)。
  3. 现在,在序列化时:

    1. std::map<int, YourBaseClass*>开头。使用game为密钥返回的值,将您的get_serialized_id()对象添加到此地图。
    2. 虽然地图包含尚未序列化的对象:
      • 拿第一个这样的物体。
      • 序列化其get_serialized_id()
      • 通过调用serialize()的实现来序列化它。它像往常一样坚持原始数据。对于通过指针可用的数据,在每个指向的对象上调用get_serialized_id(),并仅序列化从其返回的数字。另外,将该对象添加到地图中。
    3. 这将导致一堆对象被序列化(以“随机”顺序)以及每个对象的“随机”id。

      反序列化时:

      1. std::map<int, YourBaseClass*>开头。阅读已保存文件中的第一项。
      2. 对于第一个对象指向的每个对象,您知道一个唯一的id(这是您序列化的而不是指针)。从保存的文件中获取具有此ID的项目并对其进行反序列化。
      3. 递归执行此操作,直到从保存的文件中提取和反序列化所有项目。
      4. 由于每个项目都具有在上面的步骤3中反序列化的所有依赖项,因此将其实例化为对象并将其添加到地图中。
      5. 这将使您能够在给定项目ID的情况下从地图中获取指针,您现在可以使用该指针设置依赖于此项目的对象的指针成员。
      6. 当递归结束时,地图中的最后一个对象将成为你的主要“游戏”对象,其所有指针都准备好了。

答案 7 :(得分:0)

你做的是浅拷贝,如果你的MyGame课程中有指针,那么深层拷贝就是必须的! 我建议在MyGame中实现一个函数或一组函数,它们将把自己的数据保存到文件中,你只需要调用它。

答案 8 :(得分:0)

感谢大家的快速和良好的答案,但我的一个伙伴(谁在帮助我)告诉我,我们应该以另一种方式做到。

我只保存对象的基础知识,然后在函数中重新创建其余部分。

这是一个纸牌游戏,为了保存这些卡片,我们只保存卡片的ID(不是对象),只是在我们从文件中读取ID时重新初始化每张卡片。