在Delphi中使用旧式的object(对象)而不是class(类)是否有好处? [英] Is there a benefit in using old style `object` instead of `class` in Delphi?

查看:81
本文介绍了在Delphi中使用旧式的object(对象)而不是class(类)是否有好处?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Delphi中,健全人们使用来定义对象。

在Windows的Turbo Pascal中,我们使用 object ,今天您仍然可以使用 object 创建对象。

In Delphi sane people use a class to define objects.
In Turbo Pascal for Windows we used object and today you can still use object to create an object.

不同之处在于对象驻留在堆栈中,而驻留在堆上。

当然,对象已贬值。

The difference is that a object lives on the stack and a class lives on the heap.
And of course the object is depreciated.

放所有内容:

是否有好处?可以通过使用 object 而不是类来提高速度?

is there a benefit to be had, speed wise by using object instead of class?

我知道 object 在Delphi 2009中已损坏,但我有一个特殊的用例 1),其中速度很重要,我正在尝试查找是否使用 object 可以使我的事情变得更快而又不会出错

此代码库位于Delphi 7中,但我可以将其移植到Delphi 2007中,尚未决定。

I know that object is broken in Delphi 2009, but I've got a special use case1) where speed matters and I'm trying to find if using object will make my thing faster without making it buggy
This code base is in Delphi 7, but I may port it to Delphi 2007, haven't decided yet.

1)康威的人生游戏

长评论

感谢所有人为我指出正确的方向。

Long comment
Thanks all for pointing me in the right direction.

让我解释一下。我正在尝试更快地实现 hashlife 另请参见此处此处是简单的源代码

Let me explain a bit more. I'm trying to do a faster implementation of hashlife, see also here or here for simple sourcecode

当前记录的保存者是 golly ,但是golly使用Bill Gospher原始lisp代码的直接翻译(这是一种出色的算法,但根本没有在微观层面进行优化)。 Hashlife使您能够计算O(log(n))时间的世代。

The current record holder is golly, but golly uses a straight translation of Bill Gospher original lisp code (which is brilliant as an algorithm, but not optimized at the micro level at all). Hashlife enables you to calculate a generation in O(log(n)) time.

它是通过使用时空权衡来实现的。因此,hashlife需要大量内存,千兆字节并非闻所未闻。作为回报,您可以在o(1)时间中使用2 ^ 127(170141183460469231731687303715880000000)生成2 ^ 128(340282366920938463463374374607431770000000)。

It does this by using a space/time trade off. And for this reason hashlife needs a lot of memory, gigabytes are not unheard of. In return you can calculate generation 2^128 (340282366920938463463374607431770000000) using generation 2^127 (170141183460469231731687303715880000000) in o(1) time.

由于hashlife需要计算以较大模式出现的所有子模式的哈希值,因此对象的分配需要快速。

Because hashlife needs to compute hashes for all sub-patterns that occur in a larger pattern, allocation of objects needs to be fast.

这是我已经解决的解决方案:

Here's the solution I've settled upon:

分配优化

我分配了一大笔钱物理内存块(用户可设置)为512MB。在此Blob中,我分配了称为 奶酪堆栈的内容。这是我推入并弹出的普通堆栈,但是弹出也可以从堆栈的中间开始。如果发生这种情况,我将其标记在免费列表中(这是正常的堆栈)。推送时,如果没有空闲的东西,我会先检查 free 列表,我会像往常一样推送。我将使用记录,因为它看起来像是开销最少的解决方案。

Allocation optimization
I allocate one big block of physical memory (user settable) lets say 512MB. Inside this blob I allocate what I call cheese stacks. This is a normal stack where I push and pop, but a pop can also be from the middle of the stack. If that happens I mark it on the free list (this is a normal stack). When pushing I check the free list first if nothing is free I push as normal. I'll be using records as advised it looks like the solution with the least amount of overhead.

由于hashlife的工作方式,很少发生 pop ping和很多 es。我为不同大小的结构保留了单独的堆栈,确保将内存访问对齐在4/8/16字节边界上。

Because of the way hashlife works, very little popping takes place and a lot of pushes. I keep separate stacks for structures of different sizes, making sure to keep memory access aligned on 4/8/16 byte boundaries.

其他优化


  • 递归删除

  • 缓存优化

  • 使用内联

  • 预先计算哈希(类似于彩虹表)

  • 检测病理病例并使用后备算法

  • 使用GPU

  • recursion removal
  • cache optimization
  • use of inline
  • precalculation of hashes (akin to rainbow tables)
  • detection of pathological cases and use of fall-back algorithm
  • use of GPU

推荐答案

对于使用普通的OOP编程,您应始终使用种类。您将拥有Delphi中最强大的对象模型,包括接口和泛型(在更高的Delphi版本中)。

For using normal OOP programming, you should always use the class kind. You'll have the most powerful object model in Delp including interface and generics (in later Delphi versions).

1。记录,指针和对象

记录可能是邪恶的(如果您忘记将参数声明为<$ c,则隐藏副本的速度很慢$ c> const ,记录隐藏的慢速清理代码, fillchar 会使记录中的任何字符串成为内存泄漏...),但是它们

Records can be evil (slow hidden copy if you forgot to declare a parameter as const, record hidden slow cleanup code, a fillchar would make any string in record become a memory leak...), but they are sometimes very convenient to access a binary structure (e.g. some "smallish value"), via a pointer.

微小记录的动态数组(例如,带有一个整数和一个整数),有时通过指针访问二进制结构(例如,一些较小的值)非常方便。双字段)将比小类的 TList 快得多;与我们的 TDynArray 包装器,您将可以对记录进行高层访问,包括序列化,排序,散列等。

A dynamic array of tiny records (e.g. with one integer and one double field) will be much faster than a TList of small classes; with our TDynArray wrapper, you will have high-level access to the records, with serialization, sorting, hashing and such.

如果使用指针,则必须知道您在做什么。如果您要使用神奇的 VCL组件所有权模型,则最好坚持使用类,并 TPersistent

If using pointers, you must know what you are doing. It's definitively more preferable to stick with classes, and TPersistent if you want to use the magical "VCL component ownership model".

记录不允许继承。您需要使用变体记录(在其类型定义中使用 case 关键字),或者使用嵌套记录。使用类似C的API时,有时您必须使用面向对象的结构。恕我直言,使用嵌套记录或变体记录比老式的对象继承模型还不清楚。

Inheritance is not allowed for records. You'll need either to use a "variant record" (using the case keyword in its type definition), either use nested records. When using C-like API, you'll sometimes have to use object-oriented structures. Using nested records or variant records is IMHO much less clear than the good old "object" inheritance model.

2。何时使用对象

但是在某些地方,对象是访问现有数据的好方法。

But there are some places where objects are a good way of accessing already existing data.

对象模型甚至比新记录模型更好,因为它处理简单的继承。

Even the object model is better than the new record model, because it handles simple inheritance.

去年夏天的博客条目,我发布了一些仍可以使用对象的可能性:

In a Blog entry last summer, I posted some possibilities to still use objects:


  • 我想非常快速地解析一个内存映射文件:指向这样一个对象的指针非常棒,并且您仍然有方法。我将它用于在SynZip.pas中映射.zip标头的TFileHeader或TFileInfo;

  • A memory mapped file, which I want to parse very quickly: a pointer to such an object is just great, and you still have methods at hand; I use this for TFileHeader or TFileInfo which map the .zip header, in SynZip.pas;

一个Win32结构,由API调用定义,其中我放置了方便的方法来方便地访问数据(为此,您可以使用记录,但是如果结构中存在某种面向对象的方法(这很常见,则必须嵌套记录,这不是很方便));

A Win32 structure, as defined by a API call, in which I put handy methods for easy access to the data (for that you may use record but if there is some object orientation in the struct - which is very common - you'll have to nest records, which is not the very handy);

在堆栈上定义的临时结构,仅在过程中使用:我将其用于SynZip.pas中的TZStream,或用于与RTTI相关的类,将其映射Delphi以面向对象的方式生成RTTI,而不是以函数/过程为导向的TypeInfo。通过直接映射RTTI内存内容,我们的代码比使用在堆上创建的新RTTI类更快。我们不会实例化任何内存,对于像我们这样的ORM框架来说,内存对其速度是有好处的。我们需要大量RTTI信息,但我们需要快速,直接需要它。

A temporary structure defined on the stack, just used during a procedure: I use this for TZStream in SynZip.pas, or for our RTTI related classes, which map the Delphi generated RTTI in an Object-Oriented way not as the TypeInfo which is function/procedure oriented. By mapping the RTTI memory content directly, our code is faster than using the new RTTI classes created on the heap. We don't instanciate any memory, which, for an ORM framework like ours, is good for its speed. We need a lot of RTTI info, but we need it quick, we need it directly.

3 。在现代的Delphi中如何破坏对象实现

在现代的Delphi中,对象破碎的事实令人遗憾,恕我直言。

The fact that object is broken in modern Delphi is a shame, IMHO.

通常,如果您在堆栈上定义一个包含一些引用计数变量(如字符串)的记录,它将在方法/函数的开始级别由一些编译器魔术代码初始化:

Normally, if you define a record on the stack, containing some reference-counted variables (like a string), it will be initialized by some compiler magic code, at the begin level of the method/function:

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

那些 _InitializeRecord _FinalizeRecord 将准备然后释放 O.Str变量。

Those _InitializeRecord and _FinalizeRecord will "prepare" then "release" the O.Str variable.

在Delphi 2010中,我发现有时_InitializeRecord()并非总是如此。
如果记录中只有一些公共字段,则隐藏的调用有时不会由编译器生成。

With Delphi 2010, I found out that sometimes, this _InitializeRecord() was not always made. If the record has only some no public fields, the hidden calls are sometimes not generated by the compiler.

只需再次构建源,就会有...

Just build the source again, and there will be...

我发现的唯一解决方案是使用record关键字而不是object。

The only solution I found out was using the record keyword instead of object.

所以在这里生成的代码如下所示:

So here is how the resulting code looks like:

/// 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}记录{$ else}对象{$ endif } 太糟糕了...但是此后未发生代码生成错误。.

The {$ifdef UNICODE}record{$else}object{$endif} is awful... but the code generation error didn't occur since..

在源代码中产生的修改并不庞大,但有点令人失望。我发现旧版本的IDE(例如Delphi 6/7)无法解析此类声明,因此类层次结构将在编辑器中被破坏...:(

The resulting modifications in the source code are not huge, but a bit disappointing. I found out that older version of the IDE (e.g. Delphi 6/7) are not able to parse such declaration, so the class hierarchy will be broken in the editor... :(

向后兼容性应包括回归测试。由于已有代码,许多Delphi用户仍然使用此产品。破坏功能对于Delphi的未来非常有问题,恕我直言:如果您必须重写大量代码,则应该您不只是将项目切换到C#还是Java?

Backward compatibility should include regression tests. A lot of Delphi users stay to this product because of the existing code. Breaking features are very problematic for the Delphi future, IMHO: if you have to rewrite a lot of code, which shouldn't you just switch the project to C# or Java?

这篇关于在Delphi中使用旧式的object(对象)而不是class(类)是否有好处?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆