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

发布于 2024-10-05 00:36:04 字数 247 浏览 0 评论 0原文

我知道我可以使用:

MyGame  game; // the game object
//

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

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

以及:如何解决这个问题?

I know I can use:

MyGame  game; // the game object
//

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

to save and load the game, but what if I have pointers inside MyGame structure? will the pointers just be saved but not the data it points to?

and: how to solve this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

神爱温柔 2024-10-12 00:36:04

您不能只编写指向流的指针并期望它能够神奇地完成。您需要在对象中实现保存/加载方法。例如:

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

You can't just write pointers to a stream and expect it to be magically done. You need to implement save/load methods in your objects. 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
不…忘初心 2024-10-12 00:36:04

假设您没有覆盖 char * 强制转换,是的,这很可能只保存指针而不保存数据。

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

您可以在 wikipedia 上阅读有关序列化的更多信息

Assuming you have not overridden char * cast, yes this will most probably save only pointer and not data.

What you need is Serialization of your object. You can provide a method to marshal the state of object in a bit stream and write that out. And you also need to have method to restore the state back.

You may read more about serialization on wikipedia

病毒体 2024-10-12 00:36:04

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

这是一个相当广泛的库,但您不需要编写那么多代码就可以在自己的项目中开始使用它。在我看来,除了最简单的序列化要求之外,其他任何事情都值得学习。

Boost has a serialization library, with built in support for deep pointer save and restore, and proper serialization of pointers to shared data.

It's a rather extensive library, but you don't need to write that much code to start using it in your own projects. Well worth the learning effort for anything but the simplest serialization requirements in my opinion.

暮年慕年 2024-10-12 00:36:04

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

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

#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;
}

您必须了解这种方法的问题,例如生成的文件将是特定于平台的(即不可移植的)等。

You could overload the stream out operator (<<) and stream out each individual field (and vice versa)

EDIT: here is a complete example...

#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;
}

You must understand the issues with this approach though, such as the resulting file will be platform specific (i.e. non-portable) etc.

撧情箌佬 2024-10-12 00:36:04

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

Try game.serialize(out);. In your serialize member function call serialize of your pointer members.

糖果控 2024-10-12 00:36:04

为需要持久化的每种类型创建一个序列化函数。
为每个成员调用这个。

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

boost.serialize 可以帮助你。

Make a serializing function per type that needs to be persistent.
Call this for each member.

It is actually similar to serializing over network or visualizing for debug-purposes.

boost.serialize can help you.

昨迟人 2024-10-12 00:36:04

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

解决此类问题的一般方法如下:

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

现在,在序列化时:

  1. std::map 开始。使用 get_serialized_id() 返回的值作为键,将您的 game 对象添加到此地图。
  2. 虽然映射包含尚未序列化的对象:
    • 获取第一个这样的对象。
    • 序列化其 get_serialized_id()
    • 通过调用其 serialize() 实现来序列化它。让它像往常一样保留其原始数据。对于通过指针提供的数据,请对指向的每个对象调用 get_serialized_id() ,然后序列化从该对象返回的数字。另外,将该对象添加到地图中。

这将导致一堆对象被序列化(以“随机”顺序)以及每个对象的“随机”id。

反序列化时:

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

"Naive" serialization that just dumps the value of pointers is never going to work because, when deserializing, those pointers will be invalid.

The general approach to this kind of problem would go like this:

  1. Have each object that can be serialized in your game implement a serialize() virtual function (I am assuming all such objects will ultimately derive from the same base class).
  2. Have the base class implement a get_serialized_id() public function. This function will use a static auto-incremented variable to generate a unique id for each object instance, but only the first time it is called (subsequent calls will just return the existing value).

Now, when serializing:

  1. Start with a std::map<int, YourBaseClass*>. Add your game object to this map, using the value returned by get_serialized_id() for the key.
  2. While the map contains objects that have not been serialized yet:
    • Take the first such object.
    • Serialize its get_serialized_id().
    • Serialize it by calling its implementation for serialize(). Have it persist its primitive data as usual. For data available through pointers, call get_serialized_id() on each object pointed to and just serialize the number returned from it. Also, add that object to the map.

This will result in a bunch of objects being serialized (in a "random" order) along with each one's "random" id.

When deserializing:

  1. Start with a std::map<int, YourBaseClass*>. Read the first item in your saved file.
  2. For each object pointed to by this first object, you know a unique id (this is what you serialized instead of a pointer). Fetch the item with this id from the saved file and deserialize it.
  3. Recursively do this until all items have been fetched and deserialized from the saved file.
  4. As each item has all its dependences deserialized in step 3 above, instantiate it as an object and add it to the map.
  5. This will enable you to grab a pointer from the map given an item's id, which you can now use to set the pointer members of objects dependent on this item.
  6. When the recursion ends, the last object in the map will be your main "game" object with all its pointers ready to go.
Hello爱情风 2024-10-12 00:36:04

您所做的是浅复制,如果您的 MyGame 类中有指针,那么深复制是必须的!
我建议在 MyGame 中实现一个或一组函数,负责将其自己的数据保存到文件中,您只需要调用它即可。

What you did is shallow copy, if you have pointers in your MyGame class, then a deep copy is a MUST!.
I suggest implementing a function or a set of functiions inside MyGame that will take care of saving its own data to a file,and you will only need to call it.

囍孤女 2024-10-12 00:36:04

感谢大家快速而好的答案,但是我的一个朋友(正在帮助我的人)告诉我我们应该以另一种方式来做。

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

这是一款纸牌游戏,为了保存纸牌堆,我们将仅保存纸牌的 ID(而不是对象),并在从文件中读取 ID 时重新初始化每张纸牌。

Thanks everyone for the fast and good answers, but a buddy of mine (who is helping me on this) told me we should do it in another way.

I just save the basics of the object and we recreate the rest in a function.

It's a card-game and to save the stack of cards we'll be saving the ID of the card only (not the objects) and just re-initializing each card when we read in the ID from the file.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文