微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在Delphi中使用旧式的object对象而不是class类是否有好处?

如何解决在Delphi中使用旧式的object对象而不是class类是否有好处?

| 在Delphi中,理智的人使用ѭ0来定义对象。 在Windows的Turbo Pascal中,我们使用used1ѭ,今天您仍然可以使用
object
创建对象。 区别在于,ѭ1驻留在堆栈上,a0驻留在堆栈上。 当然,ѭ1也会贬值。 抛开所有这些: 通过使用
object
而不是class来获得速度方面的好处? 我知道ѭ1在Delphi 2009中是坏的,但是我有一个特殊的用例1)速度很重要,我试图找出是否使用ѭ1可以使我的事情变得更快而又不会出错 该代码库位于Delphi 7中,但我尚未移植到Delphi 2007中。 1)康威的人生游戏 长评论 感谢所有人为我指明正确的方向。 让我解释一下。我正在尝试更快地实现hashlife,请参见此处或此处以获取简单的源代码 当前的记录保存者是golly,但是golly使用Bill Gospher原始lisp代码的直接翻译(这是一种出色的算法,但根本没有在微观层面进行优化)。使用Hashlife,您可以计算O(log(n))时间的世代。 它通过使用时空权衡来做到这一点。因此,hashlife需要大量内存,千兆字节并非闻所未闻。作为回报,您可以在o(1)时间中使用2 ^ 127(170141183460469231731687303715880000000)生成2 ^ 128(340282366920938463463463374607431770000000)生成。 由于hashlife需要计算以较大模式出现的所有子模式的哈希,因此对象的分配需要快速。 这是我确定的解决方案: 分配优化 我分配了一大块物理内存(用户可设置),比如说512MB。在这个Blob中,我分配了我所谓的奶酪堆。这是我推入并弹出的普通堆栈,但是弹出也可以从堆栈的中间开始。如果发生这种情况,我将其标记在“ 9”列表上(这是正常的堆栈)。推送时,如果没有空闲的东西,我会先检查
free
列表,然后按常规推送。我将按照建议使用记录,它看起来像是开销最少的解决方案。 由于hashlife的工作方式,很少发生ѭ11ping,而发生很多
push
。我为不同大小的结构保留了单独的堆栈,请确保将内存访问对齐在4/8/16字节边界上。 其他优化 递归删除 缓存优化 使用
inline
哈希的预先计算(类似于彩虹表) 病理病例的检测和回退算法的使用 使用GPU     

解决方法

要使用普通的OOP编程,应始终使用
class
类型。您将拥有Delphi中最强大的对象模型,包括接口和泛型(在更高的Delphi版本中)。 1.记录,指针和对象 记录可能是邪恶的(如果您忘了将参数声明为ѭ15s,则会隐藏慢速副本,记录隐藏的慢速清理代码,
fillchar
会使记录中的任何字符串都成为内存泄漏...),但是有时访问它们非常方便通过指针的二进制结构(例如某些“较小的值”)。 小型记录的动态数组(例如具有一个整数和一个双字段)将比小类的“ 17”个要快得多;使用我们的
TDynArray
包装器,您将可以对记录进行高层访问,包括序列化,排序,散列等。 如果使用指针,则必须知道您在做什么。坚持使用类绝对是更可取的选择,如果您想使用神奇的“ VCL组件所有权模型”,则使用“ 19”。 记录不允许继承。您将需要使用\“ variant record \”(在其类型定义中使用
case
关键字),或者使用嵌套记录。使用类似C的API时,有时必须使用面向对象的结构。恕我直言,使用嵌套记录或变体记录比旧的“对象”继承模型要清晰得多。 2.何时使用物体 但是在某些地方,对象是访问已经存在的数据的好方法。 甚至对象模型也比新记录模型更好,因为它处理简单的继承。 在去年夏天的博客文章中,我发布了一些仍然可以使用对象的可能性: 我想非常快速地解析一个内存映射文件:指向这样一个对象的指针非常棒,并且您仍然有方法。我将它用于在SynZip.pas中映射.zip标头的TFileHeader或TFileInfo; 由API调用定义的Win32结构,在其中放置了方便的方法以方便地访问数据(为此,您可以使用记录,但是如果结构中存在某些面向对象的情况-这很常见-您将必须嵌套记录,这不是很方便); 在堆栈上定义的临时结构,仅在过程中使用:我将其用于SynZip.pas中的TZStream,或用于我们的RTTI相关类,这些类以面向对象的方式映射Delphi生成的RTTI,而不是用作函数的TypeInfo /面向过程。通过直接映射RTTI内存内容,我们的代码比使用在堆上创建的新RTTI类更快。我们不会实例化任何内存,对于像我们这样的ORM框架而言,内存对其速度是有好处的。我们需要大量的RTTI信息,但是我们需要快速,直接的信息。 3.在现代Delphi中如何破坏对象实现 在现代的Delphi中对象被破坏的事实真是可惜,恕我直言。 通常,如果您在堆栈上定义一个记录,其中包含一些引用计数的变量(例如字符串),那么它将在方法/函数的开始级别由一些编译器魔术代码初始化:
type TObj = object Int: integer; Str: string; end;
procedure Test;
var O: TObj
begin // here,an _InitializeRecord(@O,TypeInfo(TObj)) call is made
  O.Str := \'test\';
  (...)
end;  // here,a _FinalizeRecord(@O,TypeInfo(TObj)) call is made
那些“ 22”和“ 23”将“准备”然后“释放” O.Str变量。 在Delphi 2010中,我发现有时_InitializeRecord()并非总是如此。 如果记录中只有一些没有公共字段,则编译器有时不会生成隐藏的调用。 只要重新构建源,就会有... 我发现的唯一解决方案是使用record关键字而不是object。 因此,结果代码如下所示:
/// used to store and retrieve Words in a sorted array
// - is defined either as an object either as a record,due to a bug
// in Delphi 2010 compiler (at least): this structure is not initialized
// if defined as a record on the stack,but will be as an object
TSortedWordArray = {$ifdef UNICODE}record{$else}object{$endif}
public
  Values: TWordDynArray;
  Count: integer;
  /// add a value into the sorted array
  // - return the index of the new inserted value into the Values[] array
  // - return -(foundindex+1) if this value is already in the Values[] array
  function Add(aValue: Word): PtrInt;
  /// return the index if the supplied value in the Values[] array
  // - return -1 if not found
  function IndexOf(aValue: Word): PtrInt; {$ifdef HASINLINE}inline;{$endif}
end;
{$ifdef UNICODE}record{$else}object{$endif}
太糟糕了...但是自此以来未发生代码生成错误。 在源代码中产生的修改不是很大,但是有点令人失望。我发现旧版本的IDE(例如Delphi 6/7)无法解析此类声明,因此类层次结构将在编辑器中被破坏... :( 向后兼容性应包括回归测试。由于已有代码,许多Delphi用户仍然选择使用此产品。恕我直言,中断功能对于Delphi的未来非常成问题:如果您必须重写大量代码,那您不应该只是将项目切换到C#或Java吗?     ,
Object
不是Delphi 1设置对象的方法;它是短暂的建立对象的Turbo Pascal方法,在Delphi 1中被Delphi TObject模型所取代。保留该方法是为了向后兼容,但出于以下几个原因应避免使用: 如您所述,它在最新版本中已损坏。而且AFAIK没有修复它的计划。 这是一个概念上错误的对象模型。面向对象程序设计的真正要点是,它与过程程序程序的真正区别是Liskov替换(继承和多态性),并且继承和值类型不混合。 您失去了对许多需要TObject后代的功能的支持。 如果您确实需要不需要动态分配和初始化的值类型,则可以使用记录。您不能从它们那里继承,但是用with1ѭ也不能很好地做到这一点,因此您在这里不会失去任何东西。 至于其余的问题,没有那么多速度优势。 TObject模型非常快,尤其是当您使用FastMM内存管理器来加速对象的创建和销毁时,并且如果您的对象包含很多字段,它们在很多情况下甚至可能比记录快,因为\是通过引用传递的,不必为每个函数调用而复制。     ,如果在“快速且可能会损坏”和“快速且正确”之间进行选择,则始终选择后者。 老式对象对原始的旧记录没有提速的作用,因此,无论您在何处尝试使用老式对象,都可以使用记录,而不必担心存在未初始化的编译器管理的类型或虚方法损坏的风险。如果您的Delphi版本不支持带方法的记录,则只需使用独立过程即可。     ,回到较早版本的Delphi,该版本不支持使用方法记录的方法,然后使用
object
是在堆栈上分配对象的方法。偶尔会产生有价值的性能优势。如今,ѭ29更好。
record
唯一缺少的功能是从另一个another29ѭ继承的能力。 当您从
class
更改为give29ѭ时,您会付出很多,因此,只有在性能优势不容置疑的情况下,才考虑这样做。     

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。