如何使用HeapCreate/HeapAlloc分配类?

use*_*072 3 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.
Run Code Online (Sandbox Code Playgroud)

Rob*_*edy 10

Delphi仅提供有限的工具来从主存储器管理器以外的任何地方分配对象.为此,您需要覆盖类的NewInstance方法.这是构造函数调用以分配类的新实例的方法.破坏对手是FreeInstance.

覆盖这些方法并调用HeapAllocHeapFree.但是,要分配的字节数不是由给出的SizeOf.SizeOf告诉你对象引用的大小,它始终是SizeOf(Pointer).您需要实例的大小,该类由类的InstanceSize方法给出.

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

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

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

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


您尝试的代码存在一些问题.首先是你从未真正分配过TStringList.你分配了一个指针,如上所述,它是SizeOf(Pointer)字节.这个内存不足以容纳一个TStringList实例.

你根本不需要一个^TStringList.您可以将已分配的内存直接分配给TStringList变量SL:

SL := HealAlloc(Handle, 0, TStringList.InstanceSize);
Run Code Online (Sandbox Code Playgroud)

请注意我是如何调整大小的.

但这仍然不够,因为虽然这已经分配了一些内存,但它还没有构建对象.你需要调用构造函数.

SL.Create;
Run Code Online (Sandbox Code Playgroud)

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

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

Delphi无法在不释放相关内存的情况下销毁对象.也就是说,你不能在没有打电话的Destroy情况下打电话FreeInstance,但是TStringList.FreeInstance会打电话FreeMem,而不是HeapFree.


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

  • 如果需要每线程堆,则使用线程内存管理器.您不需要应用程序代码来指定每个分配使用哪个堆.您只需要内存管理器为每个线程使用不同的堆.当我建议你退后一步时,这就是我所说的.你问过*XY问题*. (6认同)