如何使用HeapCreate / HeapAlloc分配类?

时间:2015-11-05 20:53:11

标签: delphi delphi-xe4

如何在Delphi中使用HeapCreate和HeapAlloc分配类?以下示例将崩溃。

program Project2;

uses
  System.SysUtils,
  System.Classes,
  Winapi.Windows;

var
  SL: TStringList;
  Handle: THandle;
  P: ^TStringList;
begin
  Handle := HeapCreate(0, SizeOf(TStringList), SizeOf(TStringList));
  P := HeapAlloc(Handle, 0, SizeOf(TStringList));
  P.Add('some random string'); // crash

  HeapFree(Handle, 0, @P);
end.

1 个答案:

答案 0 :(得分:10)

Delphi仅提供有限的工具,用于从主内存管理器以外的任何位置分配对象。为此,您需要覆盖类的NewInstance方法。这是构造函数调用以分配类的新实例的方法。销毁对应物是FreeInstance

覆盖这些方法并致电HeapAllocHeapFree。但是,SizeOf不会给出要分配的字节数。 SizeOf告诉您对象引用的大小,始终为SizeOf(Pointer)。您需要实例的大小,该大小由类InstanceSize方法提供。

虽然您可以覆盖使用所选内存分配策略的方法,但您可能不会对结果感到满意,因为存在一些问题:

  1. NewInstance方法不接受任何参数,因此您无法告诉类从哪个堆分配。您可以拥有一个全局堆,或每个类一个堆,但您无法单独选择要用于每个实例的堆。

  2. NewInstance方法由给定类的所有实例共享,因此无法在特殊堆上仅分配某些实例并分配从默认的内存管理器休息。这主要是描述上一个问题的另一种方式。

  3. 您无法将其改装为现有课程,因此您可以分配TMyStringList,但不能分配TStringList。 (嗯,你可以,但它需要修补每个类的VMT,这通常是不推荐的。)

  4. 您尝试过的代码存在一些问题。首先,你从未真正分配过TStringList。您已将指针分配给一个,如上所述,它是SizeOf(Pointer)个字节。没有足够的内存来容纳TStringList个实例。

    你根本不需要^TStringList。您可以直接将分配的内存分配给TStringList变量SL

    SL := HealAlloc(Handle, 0, TStringList.InstanceSize);
    

    请注意我是如何调整尺寸的。

    但这仍然不够,因为虽然分配了某些内存,但它还没有构造该对象。您需要调用构造函数。

    SL.Create;
    

    请注意,构造函数将分配更多内存来保存字符串列表,并分配更多内存来保存字符串内容。那些内存分配不会在堆上。他们会像往常一样转到默认的内存管理器。

    销毁对象将是另一个问题。您需要调用析构函数来释放字符串,但如果您这样做,析构函数也会尝试释放对象的内存。它将使用默认内存管理器来释放它,但由于您没有使用默认内存管理器来分配TStringList对象,因此内存管理器将抛出EInvalidPointer异常。

    Delphi无法在不释放相关内存的情况下销毁对象。也就是说,如果不拨打Destroy,就无法拨打FreeInstance,但TStringList.FreeInstance会拨打FreeMem,而不是HeapFree

    我建议您从当前任务中退一步,重新检查您想要解决的问题,并考虑为某些对象创建单独的堆。可能有更好的解决方案不会花费太多精力来对抗您的工具的设计目标。