我试图用嵌套结构解析二进制文件格式。在程序伪代码中,过程就是这样:
// A structure contains:
// tag | oneof(a, b, c) | oneof(oneof(aa, ab, ac), oneof(ba, bb, bc), oneof(ca, cb, cc))
PROCEDURE parse() {
RECORD read_type;
read_tag(read_type);
if (read_type == TYPE_A) {
read_a(read_type);
if (read_type == TYPE_AA) {
read_aa();
} else if (read_type == TYPE_AB) {
read_ab();
} else if (read_type == TYPE_AC) {
read_ac();
}
} else if (read_type == TYPE_B) {
// see above
} else if (read_type == TYPE_C) {
// see above
}
}
如果没有来自其父对象A的上下文,则无法解释诸如AA的外部结构,而父对象A又需要其标记/标题来解释。使用这些结构时,操作包含A的结构,包含AA等,但不仅仅是结构的A或AA部分是有意义的。
我的问题是如何为此过程创建类模型。该结构应该是:
class Base;
class A: Base;
class B: Base;
class C: Base;
class AA: A;
class AB: A;
class AC: A;
// ...
在这种情况下,AA可以这样构建:
AA::AA(): A() {
read_aa();
}
A::A(): Base() {
read_a();
}
Base::Base() {
read_tag();
}
然而,问题在于,如果不先构建基础对象,就不可能知道要构造的派生对象。这可以通过使用复制构造其父级的构造函数AA :: AA(A *)来解决,但这似乎是不必要的低效率。此外,这需要外部工厂功能,例如:
Base *read_object() {
Base *base = new Base();
if (b->tag_type == TYPE_A) {
A *a = new A(base);
if (a->tag_type == TYPE_AA) {
return new AA(a);
} else if (a->tag_type == TYPE_AB) {
// ...
} else if (a->tag_type == TYPE_AC) {
// ...
}
} else if (b->tag_type == TYPE_B) {
// ...
} else if (b->tag_type == TYPE_C) {
// ...
}
}
另一种选择是让类引用结构的子区域,例如:
class CompleteStructure;
class StructureA;
class StructureB;
class StructureC;
class StructureAA;
class StructureAB;
class StructureAC;
// ...
class CompleteStructure {
union {StructureA a, StructureB b, StructureC c} sub;
}
class StructureA {
CompleteStructure *parent;
union {StructureAA aa, StructureAB ab, StructureAC ac} sub;
}
class StructureAA {
StructureA *parent;
}
在这种情况下,构造函数CompleteStructure :: CompleteStructure()将读取标记,然后构造StructureA,StructureB或StructureC中的一个,然后构造自己的子结构。这个问题是每个子结构都需要一个明确的引用它的父结构,以便" cast"层次结构并实现其方法/功能。
这些方法中的一种在空间/时间效率和"清洁度方面比其他方法更好吗?是否有优越的第三种方法?
编辑: 要回答下面的两个答案,问题是关于解析和对象行为。我最初的目标只是从文件中读取结构,打印出它们的字段,然后以相同的顺序将它们写回磁盘。稍后,还会有其他目标,例如查找A派生结构的所有实例,并按某些字段对其进行排序,或检查结构的非法组合(例如同时具有BA和BB)。
EDIT2: 这是我引用的结构之一的示例模式(具有通用字段名称)。 u8 / 16/32引用整数类型,sz是C字符串,大写名称是需要读取的字段,常量以下划线为前缀。
DEF AA {
// Identifies and deliminates complete records.
TAG {
u32 SYNC_CODE = 0xFFFFFFFF;
}
// Metadata for high level identification of data.
A {
u32 TYPE = __TYPE_A;
u16 CATEGORY = __CATEGORY_1; // A defines the "category" of the following file data
u32 NUM_OF_KV_PAIRS;
for (int i = 0; i < NUM_OF_KV_PAIRS; ++i) { // unspecified metadata
sz KEY;
sz VALUE;
}
u8 HAS_EXTENSION_FLAG = true; // indicates presence of next record
if (!HAS_EXTENSION_FLAG) {
DEFAULT_PARAMS; // legacy
}
}
// Indicates a specific data layout and version.
AA {
u32 TYPE = __TYPE_AA;
u8[16] ACCESS_KEY;
u32 NUM_OFFSETS;
for (int i = 0; i < NUM_OFFSETS; ++i) {
// stuff
}
}
}
答案 0 :(得分:0)
如果没有更具体的问题描述,某些方法在效率方面更好,很难回答。下面你可以找到一些值得思考的东西。
第1点:在考虑类设计时,还值得检查所需的行为,而不仅仅是数据。当然,应该考虑用于存储的二进制格式可能或可能不意味着层次结构的事实,但它不应该是主要关注点。
例如,假设我们有一个Person
类,其中height
字段和Rectangle
类也有height
字段。它们都共享一些数据但只有这些信息使它们彼此无关。如果我们定义上下文并且我们说我们想在屏幕上绘制它们,那么突然height
字段具有更具体的含义。现在继承一个Drawable
可能更有意义。
您的问题是我们将如何使用它们?如果我们有{A, B}
或{AA, BB}
或甚至{A, BB}
的列表,我们可以执行哪些常见操作?我们能以某种方式管理它们吗?这是您应该考虑的重点。
第2点:你说&#34;操纵包含A的结构,包含AA等,但不仅仅是结构的A或AA部分& #34; 。所以我理解AA
是-a A
,但相反的情况也是如此。如果是这种情况,那么将Base, A, B, C
作为抽象类是有意义的,并且只能直接实例化最后一级AA, BB
等。
第3点:另一方面,如果不同的结构只是定义了一些数据而不是某些行为,那么使用组合优于继承可能会更好。例如。我们会调用像process()
那样可以对数据进行操作的方法吗?或者我们是否希望将结构本身用作数据?
class X {
Base base;
A a;
AA aa;
process() {
// this is different than calling base.process() + a.process() + aa.process()
// do we need one over the other? both?
process(base) + process(a) + process(aa);
}
}
第4点:关于阅读时实例化的顺序,这应该不是问题。也许您可以在临时存储信息时阅读信息,并在知道其完整类型(即达到最后一级)后再实例化一个类。
我希望有帮助
答案 1 :(得分:0)
问题并没有清楚地解释你认为自己在做什么,或者实际问题是什么(即你应该做什么)。
你需要非常明确地定义A,AA,AB中哪一个实体具有自己独特的存在 - 以及子关系你应该在哪里解析。你说嵌套结构,但没有详细说明。
正如另一个提到的答案 - OO是关于行为,而不是数据建模。
严重依赖继承,特别是因为你不知道你正在构建什么,听起来像是一个完全错误。一般情况下,继承heirarchies只在你需要行为(计算或做事的方法)时才有用。可以通过一些阶级层次有效地划分行为空间并从中受益。
您的问题如上所述,只是一个解析问题。你也可以使用一个Stack和一些内部状态(比如一个最简单的StringBuilder)来读取&amp;在使用Stack推送和扩展时建立解析状态流行的嵌套级别。
事实上,上面是实现大多数解析器的好方法。
更复杂的替代方法(在解析器中也常见)是构建AST。这些是非常有效的&amp;重量轻,可打造横动。
class AstNode {
protected AstNode down; // first child.
protected AstNode across; // next sibling.
public void addChild (AstNode child) {
if (getDown() == null) {
// First Child;
this.down = child;
return;
}
// Sibling to existing Children.
AstNode last = down;
while (last.getAcross() != null)
last = last.getAcross();
last.across = child;
// done.
}
}
使用AST,您还可以将属性/成员放在NodeType,Data,Type(lexical)等上,并有效地构建一个强大的数据结构。
希望这有帮助。