【问题标题】:Are classes guaranteed to have the same organization in memory between program runs?是否保证类在程序运行之间具有相同的内存组织?
【发布时间】:2012-01-28 23:40:23
【问题描述】:

我正在尝试在我的小游戏中实现保存/加载功能。为了实现这一点,我有一个中心类,它存储游戏的所有重要变量,例如位置等。然后我将这个类作为二进制数据保存到文件中。然后只需将其加载回加载功能。这似乎在大多数情况下都有效,但是如果我更改某些内容然后尝试进行保存/加载,程序将因内存访问冲突而崩溃。那么,在程序的每次运行中,类是否保证在内存中具有相同的结构,或者数据是否可以像结构一样随机排列?

回应耶稣 - 我的意思是类中的数据,所以如果我将类保存到磁盘,当我加载它时,一切都会很好地恢复。

保存

fout.write((char*) &game,sizeof Game);

加载

fin.read((char*) &game, sizeof Game);

【问题讨论】:

  • 你是指类内部的数据还是类指针的地址?
  • 请定义您所说的“改变某些事情”是什么意思。您的意思是重新编译代码并进行更改?改变你运行程序的方式?使用相同的编译器(包括版本),在相同的架构上,使用相同的优化设置,在相同的代码上,从技术上讲,您应该在内存 AFAIK 中获得相同的结构。
  • 我的意思是更改类中的数据值。不重新编译或任何东西。

标签: c++ class memory-management load


【解决方案1】:

您的方法非常脆弱。有很多限制,它可以工作。在典型情况下,这些限制不值得让您的用户(或您自己!)接受。

一些限制:

  • 切勿引用外部存储器(例如指针或引用)
  • 禁止 ABI 更改/差异。常见情况:32 与 64 上的内存布局和自然对齐会有所不同。用户需要为每个 ABI 提供一个新的“游戏”。
  • 不兼容字节序。
  • 更改类型的布局会破坏您的游戏。更改编译器选项可以做到这一点。
  • 您基本上仅限于 POD 数据。
  • 使用偏移量而不是指针来引用内部数据(此引用将在连续内存中)。

因此,您可以在极其有限的情况下安全地使用这种方法——这通常只适用于系统的组件,而不是整个游戏状态。

由于它被标记为 C++,“boost - Serialization”将是一个很好的起点。它经过充分测试,为您抽象了许多复杂性。

【讨论】:

  • 什么是明智的选择?编写手动保存到磁盘的函数似乎很乏味,并且需要在游戏版本发生变化时进行更改。
  • @user1065671 如果你不喜欢乏味,C++ 可能不是最好的选择,但我相信有很多序列化库可以帮助你。
  • @user1065671:乏味?是的。但是,如果您想以任何甚至接近一致的方式直接保存和加载对象,这就是您必须做的事情。 Boost's Serialization library 使其尽可能轻松,但您仍然必须编写函数来序列化每个成员。 Boost.Serialization 确实有一些不错的功能,比如版本支持,以及在不干扰类内部的情况下执行此操作的能力。
  • @user1065671 我已经推荐您在编辑我的答案时检查提升序列化。由于多种原因,该主题/问题很复杂(上面概述了一些)。对序列化的强大支持并非微不足道,但一个好的序列化库可以将其抽象化。当您更改游戏的模型/版本时,您真的不需要花费太多时间重写您已经编写的内容 - 通常只有在您引入更改之后(例如,将某些内容移动到另一个类/组件或更改结构以具有不同的成员 -明智的逻辑)。
  • @Trillian C++ 中间件编写器通​​过为您创建/更新编组函数来减少您必须编写的样板代码量。
【解决方案2】:

即使这行得通,也不要这样做。在字节级别定义文件格式并编写合理的“转换为文件格式”和“从文件格式转换”函数。您实际上会知道文件的格式。您将能够扩展它。该程序的较新版本将能够从旧版本中读取文件。而且您将能够更新您的平台、构建工具和类,而不必担心导致您的程序崩溃。

【讨论】:

    【解决方案3】:

    是的,每次程序运行时,类和结构都会在内存中具有相同的布局。虽然我不能说标准是否强制执行这一点。 C++ 编译器生成的机器代码使用“硬编码”偏移来访问类型字段,因此它们是固定的。实际上,只有当您修改 C++ 类定义(字段大小、顺序、虚拟方法等)、使用不同的编译器编译或更改编译器选项时,布局才会改变。

    只要类型是 POD 并且没有指针字段,将其转储到文件并使用完全相同的程序读回应该是安全的。但是,由于上述问题,这种方法在版本控制和互操作性方面相当不灵活。

    [编辑]

    要响应您自己的编辑,请勿对您的“游戏”对象执行此操作!它当然有指向其他对象的指针,这些对象将不再存在于内存中,或者在您重新加载文件时将在其他地方。

    【讨论】:

      【解决方案4】:

      您可能想看看this

      不保证类在内存中具有相同的结构,因为每次创建类时指针可以指向内存中的不同位置。

      但是,如果不发布代码,很难确定问题出在哪里。

      【讨论】:

      • 指向内存中不同位置的指针不应更改存储它的结构,只更改此结构中的值。如何保存指向的结构是另一回事。
      猜你喜欢
      • 2013-11-17
      • 2018-05-12
      • 2022-07-27
      • 2021-10-21
      • 1970-01-01
      • 2019-02-24
      • 1970-01-01
      • 1970-01-01
      • 2018-02-04
      相关资源
      最近更新 更多