我正在尝试用C ++编写持久性数据结构,但是我觉得我应该能够使它与我的数据结构读者的各种其他实现二进制兼容,因此,我目前的想法是在本机内存中声明数据结构没有任何抽象。
例如,我将指定一个线性内存块作为数据结构(使用new
关键字),然后描述第一个字节的含义,第二个字节的含义等等。我知道我可以使用struct
执行此操作,但是,数据结构将绑定到一种语言,其他语言将必须使用此结构。此外,实现可能会从编译器更改为编译器。我宁愿把它当作记忆标准。
我想做的事情有点明智吗?或者我试图过度简化事情,应该继续使用struct
数据结构?现在进入C ++部分,如果您认为我应该使用struct
数据结构,那么使用完整类的缺点是什么?
(无论如何,我使用类来包装内存结构并为其提供函数,因为数据结构无论如何都是持久的。) 的修改 正如所建议的那样,我不需要需要围绕内存结构的任何这样的高级接口包装器,所以关于类包装器的最后一点没有正确说明。我的意思是我希望有一个用于内存表示的类接口,它不一定必须是一个包装器。
答案 0 :(得分:1)
我已阅读/使用的几种文件格式正是这样做 - 定义内存标准或布局,然后通常使用类似C的伪struct
来演示备份。有时它们会提供结构或类表示,有些则完全被库抽象化。当然,这些格式会记录所有字段,大小,数据的字节顺序等。
我认为endian相关问题,填充,复杂性(例如由数据结构的变化引入)和正确的版本控制是最大的错误来源。我发现的另一个问题是使用过去的数据结构和用于表示类似功能的数据结构的不一致性 - 您可能会收到一个规范,并意识到它包含几个不同的字符串表示 - 所有这些都是过时的,并且有人必须继续支持所有这些(双向)。
继续这条路线:
如果您不想支持它,则不应提交二进制表示(或可编译程序)(并且随着平台和工具集的更改,长期格式的尝试会失败/绊倒)。首先提交一个正式的内存标准,然后在测试和示例输入文件的基础上构建,以验证表示是否正确地序列化和反序列化。一个非常基本的测试套件将有助于确保您的模型在您需要的所有系统上都可移植,并且可以指出您可能没有意识到的潜在缺陷或特定于平台的注意事项。
如果你真的想提供一个可编辑的表示,我会坚持使用一个非常合规的struct
表示 - 客户端可以采用它(在内存中)表示并将其转换为他们喜欢的任何C ++抽象/表示。也就是说,序列化表示可能不应该反映内存中表示的表示,除了简单的表示和这种表示的中间存储(扁平化和打包的结构)。
其中一个重要的部分是你应该有一些测试来确认你使用这些结构创建的内存对象图是向前和向后可序列化和可反序列化的,并支持正确的版本控制 - 所以它通常需要一些努力使复杂的序列化表示兼容。所以你看到这种方法只是在另一个上面引入了一个抽象层。在这方面,您可能希望让C ++抽象能够从打包的内存表示中创建自己,并确保该表示也可以正确地填充打包的结构而不会丢失数据。
除此之外,还有需要拥有更高级的界面吗?如果有,那么您可能想要提供该信息。
所以,是的,内存标准是你必须得到正确和稳定的部分,并且所有实现应该引用和测试 - 无论平台/体系结构的差异如何。 IOW,你走在正确的轨道上;)
答案 1 :(得分:0)
在C ++中,struct
和class
之间没有实际区别(除struct
中的公共默认辅助功能外)。传统上,当类型只有(公共)成员变量而没有成员函数时使用struct
,但这只是一个约定,而不是编译器强制执行的规则。
我当然会使用struct
/ class
来描述数据。如果有人想要编写数据结构的读者,他们可以导入头文件或用他们选择的语言实现数据结构 - 在大多数编程语言中,这应该非常简单。
我建议你开始这样的结构:
typedef struct
{
int Version; // struct layout version
int ByteSize; // byte size of structure for validation
...
} MYDATA;
这样,当您的数据结构被传递时,您的代码可以验证分配的结构大小是否与您对结构的给定版本所期望的字节数相匹配。然后,您只需更新版本字段并检查新大小即可轻松引入结构的新版本。
当您将数据保存到磁盘时,请确保逐个字段地写出来,而不是通过一次写入(使用指针和sizeof()
来确保其他语言不必处理你的C ++编译器可能决定放入的潜在填充。可以在结构中手动布局字段,这样就没有填充,但是你必须非常非常小心,这样做很容易出错。