为什么Delphi记录不能继承?

Clo*_*737 18 delphi inheritance records class

我长期以来想知道的东西:为什么Delphi记录不能继承(因此所有其他重要的OOP功能)?

这基本上会使记录成为堆栈分配的类版本,就像C++类一样,并且会使"对象"(注意:非实例)过时.我没有看到任何问题.这也是实施记录前瞻性声明的好机会(我仍然感到困惑,为什么它仍然缺失).

你觉得这有什么问题吗?

Bar*_*lly 25

与此问题相关,有两种继承:接口继承和实现继承.

接口继承通常意味着多态性.这意味着如果B是从A派生的,那么类型B的值可以存储在类型A的位置.由于切片,这对于值类型(如记录)而不是引用类型是有问题的.如果B大于A,则将其存储在类型A的位置将截断该值 - B在其定义中添加的超出A的那些字段将丢失.

从这个角度来看,实现继承的问题较少.如果Delphi有记录继承,但只有实现,而不是接口,事情就不会太糟糕.唯一的问题是,只需将类型A的值设为B类型的字段,就可以完成实现继承所需的大部分操作.

另一个问题是虚拟方法.虚方法分派需要某种每值标记来指示值的运行时类型,以便可以发现正确的重写方法.但是记录没有任何地方存储这种类型:记录的字段是它拥有的所有字段.对象(旧的Turbo Pascal类型)可以具有虚拟方法,因为它们具有VMT:层次结构中用于定义虚拟方法的第一个对象隐式地将VMT添加到对象定义的末尾,从而增长它.但Turbo Pascal对象具有上述相同的切片问题,这使它们成为问题.值类型的虚方法有效地需要接口继承,这意味着切片问题.

因此,为了正确支持记录接口继承,我们需要某种解决切片问题的方法.拳击将是一种解决方案,但它通常要求垃圾收集可用,并且它会在语言中引入歧义,在这里可能不清楚你是在使用值还是引用 - 有点像Integer vs Java中的int与autoboxing.至少在Java中,对于盒装vs未装箱的"种类"值类型,有单独的名称.另一种进行拳击的方法就像Google Go及其接口,这是一种没有实现继承的接口继承,但需要单独定义接口,并且所有接口位置都是引用.当接口引用引用时,值类型(例如记录)被加框.而且当然,


Dav*_*vid 6

记录和类/对象是Delphi中两个截然不同的东西.基本上Delphi记录是一个C结构 - Delphi甚至支持语法来做一些事情,比如有一个可以作为4个16位整数或2个32位整数访问的记录.比如struct,record在面向对象编程之前可以追溯到语言(Pascal时代).

像结构一样,记录也是内联的内存块,而不是指向一块内存的指针.这意味着当您将记录传递给函数时,您传递的是副本,而不是指针/引用.这也意味着当你在代码中声明一个记录类型变量时,它在编译时确定它有多大 - 函数中使用的记录类型变量将在堆栈上分配(而不是作为堆栈上的指针,但是4,10,16等字节结构).这种固定大小不适合多态性.

  • 对.它们具有虚函数,但它们不支持编译器管理的类型,如接口或字段的字符串.它们在Delphi中从根本上被打破了,所以我不认为它们以任何有意义的方式存在于Delphi中.但它们在Turbo Pascal中的存在证明了它可以拥有支持继承的值类型.(向记录添加虚拟方法会比较棘手,但虚方法不是继承的基本属性.) (2认同)

Tom*_*m A 5

我看到的唯一问题(我可能是短视或错误)是目的.记录用于存储数据,而对象用于操纵和使用所述数据.为什么存储柜需要操作例程?

  • 您可以在Delphi中添加到记录中的方法只是静态方法的语法糖,就像C#中的扩展方法一样.没有多态性空间,因为没有使用虚方法表. (4认同)
  • @ Cloud737:想象一下堆栈上的一个对象,它使用RAII来管理关键部分或其他同步对象.这将是伟大的,因为为此目的使用接口(我现在这样做)引入了太多的开销.但是,为了使它有用,您需要能够准确指定何时创建和销毁它.可能在C++中,在具有当前规则的Delphi中不是这样. (2认同)
  • Delphi已经拥有继承,VMT和DMT的"记录".它们由旧的"object"关键字声明.但我不建议人们使用旧的基于堆栈的对象,因为如果您尝试使用TurboPascal 7.0之后引入的代码构造(例如属性),编译器会有一些codegen错误. (2认同)

Mas*_*ler 5

你是对的,在记录中添加继承本质上会将它们变成C++类.这就是你的答案:它没有完成,因为那将是一件可怕的事情.你可以有堆栈分配的值类型,或者你可以有类和对象,但混合这两个是一个非常糟糕的主意.一旦你这样做,你最终会遇到各种各样的生命周期管理问题,并最终不得不在语言中构建像C++的RAII模式这样丑陋的黑客来处理它们.

结论:如果您想要一种可以继承和扩展的数据类型,请使用类.这就是他们的目的.

编辑:为了回应Cloud的问题,这不是一个可以通过一个简单的例子来证明的东西.整个C++对象模型都是一场灾难.它可能看起来不像一个近距离; 你必须了解几个相互关联的问题才能真正掌握全局.RAII只是金字塔顶端的混乱.如果我有时间,也许本周晚些时候我会在我的博客上写一个更详细的解释.

  • -1错误地认为RAI​​I是一个必要但丑陋的黑客. (5认同)
  • 接口继承与值类型,即使不同大小的值类型成为彼此的子类型,以便切片进入图片,是一个非常糟糕的主意.介绍vtable,事情变得更糟:你可能最终得到的类型对于vtable引用的某些方法来说太小了,你可能会遇到覆盖堆栈的缓冲区溢出问题.接口继承与值类型:只说不. (2认同)

Mih*_*ela 5

因为记录没有VMT(虚方法表).