使用序列化 C++ 保存游戏状态
Posted
技术标签:
【中文标题】使用序列化 C++ 保存游戏状态【英文标题】:Saving a game state using serialization C++ 【发布时间】:2014-04-29 06:54:03 【问题描述】:我有一个名为 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
。
谢谢。
【问题讨论】:
你有办法将单个 A 或 C 写入磁盘吗?也许你应该先解决这个问题。 您是否有理由使用指针使事情复杂化?在任何情况下,首先为单个A
和C
编写代码,然后存储元素计数,然后存储元素。要加载,请读取元素计数和循环。
【参考方案1】:
我不知道如何实现类A
、C
或D
,但这是第一个问题:如何序列化该类的对象。对于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;
;
我知道你还有很多事情需要解决。我建议您进行调查以解决这些问题,以便您了解如何解决您的问题。
【讨论】:
【参考方案2】:您不仅要保存指针,还要尝试保存 shared_ptr
,但使用了错误的大小。
您需要为所有类编写序列化函数,注意不要只编写非 POD 类型的原始位。始终为所有内容实现逐个成员序列化是最安全的,因为您永远不知道未来会带来什么。 然后处理它们的集合只是存储有多少的问题。
C
s 的示例:
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));
;
当然,所有的错误处理都丢失了——你需要添加它。
至少从文本存储开始(使用<<
和>>
)而不是二进制文件实际上是个好主意。当您可以在文本编辑器中编辑它时,更容易找到错误或弄乱保存的状态。
【讨论】:
【参考方案3】:编写自己的序列化是一个相当大的挑战。即使您不使用 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); //
;
我不知道你为什么要对自己这样做而不是使用 boost,但这些是你应该考虑的一些情况: - 类型 - 你必须想办法知道你实际上有什么“变化类型”。 - 一个字符串(向量,随便) - 大小 + 数据(那么你从字符串中读回的第一件事就是长度,你调整它的大小并复制“长度”个字符) - 一个指针 - 保存指针指向的数据,然后在反序列化时,您必须分配它,构造它(通常是默认构造)并读回数据并将成员重置为各自的值。注意:您必须避免内存泄漏。 - 多态指针 - 哎呀你必须知道指针实际指向什么类型,你必须构造派生类型,保存派生类型的值......所以你必须保存类型信息 - 空指针...你必须区分空指针,这样你就知道你不需要进一步从流中读取数据。 - 版本控制 - 添加/删除字段后,您必须能够读取数据
内容太多,您无法获得完整的答案。
【讨论】:
以上是关于使用序列化 C++ 保存游戏状态的主要内容,如果未能解决你的问题,请参考以下文章