什么时候在教堂中使用Record
类型与类更有利? 在IRC上,有人提到记录在语言环境中分布得更好。
Chapel 中的记录和类相似,因为它们都支持创建具有字段和方法的对象。 也就是说,也有一些主要差异。 以下是对其中一些差异的快速回顾(tl;dr:记录大致类似于 C 结构,而类支持类层次结构、动态调度等),然后是一个摘要,其中给出了一些关于何时可以选择使用其中一个的指示:
变量的性质
记录- 变量直接表示记录对象 类
- 变量表示对类对象的引用(指针)
换句话说,记录变量是记录对象,因此两者之间没有区别。 相反,类变量指向一个类对象,该对象可能存储在与变量本身不同的语言环境中(即,类变量可以引用存储在分布式内存中的对象)。
这意味着,当一条记录分配给另一条记录时,RHS 记录的值将复制到 LHS 记录的值(例如,通常将 RHS 记录的字段复制到 LHS 记录的字段中)。 另一个含义是记录必须定义(或使用编译器提供的)副本初始值设定项,并且通常提供 0 参数初始值设定项来建立新的记录对象。
相反,当一个类分配给另一个类时,它使 LHS 类变量引用(点)与 RHS 类变量相同的对象。 此外,由于类变量是指针,因此可以选择将nil
值存储为哨兵(尽管Chapel也支持不可为空的类变量,以避免运行时检查的需要或零指针取消引用的风险)。
/取消分配
- 类对象在堆上分配,可由各种内存管理策略控制
- 记录对象被"就地"分配(例如,在当前堆栈帧上),并自动取消分配(例如,当它们超出范围时)
这意味着类对象的生存期可以独立于程序的结构,它依赖于几个策略之一来管理其内存(owned
、shared
、borrowed
、unmanaged
)。 相反,记录对象可以被视为始终自动内存管理,但受词法范围的约束。
举个具体的例子,如果你有一个类数组,你通常会创建一个连续的指针序列,这些指针可能引用位于系统上任何位置的对象(本地堆或其他一些区域设置的堆)。 而如果您有记录数组,则其存储通常是记录对象的连续序列。
这意味着,如果你想在Chapel中创建一个"基于指针"的数据结构(如链表,树或图形),你通常会使用类对象来存储该数据结构的节点,其中类类型的字段引用它们的邻居(而使用记录表示这些节点具有挑战性,因为Chapel没有指针,它们是就地分配的;换句话说, Chapel 记录不能包含其自己的类型字段,因为它会有效地导致无限递归的数据结构)。
继承/方法调度
- 一个类可以声明为另一个类的子类,支持虚拟方法/动态调度
- 记录不支持对象层次结构,也不支持虚拟/动态调度
这意味着,如果要像在 Java 或 C++ 中那样创建对象层次结构,则需要使用类,并且此类类层次结构的初始值设定项本身就是分层的。
总结
鉴于这些区别,在以下情况下,您通常需要使用类:
- 您想创建一个"基于指针"的数据结构(例如,链表或二叉树),因为类类型可以使用类变量字段相互引用
- 要创建支持动态方法调度的对象层次结构
- 您希望使用不受其词法范围约束的对象
- 您想要使用身份很重要的对象
在以下情况下,您通常需要使用记录:
- 价值比身份更重要,复制该值是可以的
- 您希望对象内存由变量的作用域管理(尽管
owned
类也可以具有此效果) - 您希望更好地控制对象在内存中的布局方式
在实践中,将两者结合起来可能非常强大。 例如,具有类字段的记录可用于创建引用计数对象,方法是让类实现对象的标识,记录通过赋值重载和复制初始值设定项实现引用计数语义,以处理记录副本进入和离开范围、分配等情况。 (但是,请注意,Chapel 中的shared
类直接提供此功能,因此这只是一个说明,而不是常见的做法)。