Delphi的`New`分配的内存是否可以全局访问`Dispose`?

时间:2013-09-12 00:19:42

标签: delphi

我正在考虑来自Delphi的NewDispose命令,并且想知道我是否可以在我的进程中的其他过程/函数/线程等中使用这些命令。

我想将地址存储到TList,但我有点不安全,因为它使用了var引用,可用于“保存”vars的实际地址。我不希望任何访问违规或任何事情...... 这是我的代码:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    MyTList       : TList;
  public
    { Public declarations }
  end;

var
  Form1         : TForm1;

type
  TMyStruct = record
    Int1        : Integer;
    Int2        : Integer;
    Str1        : String;
    Str2        : String;
end;


implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  P_TMyStruct : ^TMyStruct;
  I           : Integer;
begin
  for I := 1 to 3 do begin
    New (P_TMyStruct);
    P_TMyStruct^.Int1 := I;
    P_TMyStruct^.Int2 := 1337;
    P_TMyStruct^.Str1 := inttostr(I);
    P_TMyStruct^.Str2 := '1337';
    MyTList.Add(P_TMyStruct);
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  I : Integer;
begin
  for I := 0 to MyTList.Count - 1 do begin
    ShowMessage(inttostr(TMyStruct(MyTList.Items[i]^).Int1));
    ShowMessage(inttostr(TMyStruct(MyTList.Items[i]^).Int1));
    ShowMessage(TMyStruct(MyTList.Items[i]^).Str1);
    ShowMessage(TMyStruct(MyTList.Items[i]^).Str2);
    Dispose(MyTList.Items[i]);
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  MyTList := TList.Create;
end;

end.

那么这是安全的,因为我没有把它丢弃在堆栈中吗?

1 个答案:

答案 0 :(得分:5)

Delphi中的

动态内存分配将返回上的内存,而不是堆栈。 (请参阅这些术语的Marco Cantu's explanation。)因此,在您的应用程序 中,内存将“全局可访问”,前提是该内存可用 New()过程是动态分配内存的一种方法。 (其他一些是:GetMem(),字符串赋值,对象创建。)

所以你提议的是安全的(不会导致访问违规) 但是,由于您使用它的方式,它会泄漏内存,Dispose()不会释放所有内存。

为结构分配字符串值时,会在堆上分配更多内存。当你稍后Dispose你的结构时,New()中分配的确切内存被释放,但是在低级别,(简单的指针指向内存),Delphi不知道可能有其他内部结构需要解除分配;所以字符串泄露了。

修复内存泄漏

您需要做的是将MyTList.Items[i]返回的指针强制转换为正确的类型。但首先,您需要显式声明指向结构的指针的类型。作为标准惯例,大多数Delphi程序员将声明具有相同名称的指针类型,用P. I.e替换T前缀。

PMyStruct = ^TMyStruct;
TMyStruct = record
  Int1        : Integer;
  Int2        : Integer;
  Str1        : String;
  Str2        : String;
end;

然后,当您执行以下操作:Dispose(PMyStruct(MyTList.Items[i]))时,编译器会“识别”指针所指的类型,并将使用该信息为其托管类型执行其他操作。重点是编译器可以正确地自动处理托管类型 - 但是如果你给它提供有关包含它们的结构的正确信息,那么只能

受此影响的类型包括:

  • 字符串
  • 动态数组
  • 接口引用(需要ref发布)
  • 此外,还将管理对上述托管类型的任何间接引用。 E.g。
  • 如果是VariantOleVariant引用和界面,则会对其进行管理。
  • 如果记录(结构)中的子记录引用托管类型,则将对其进行管理。
  • 字符串数组,接口数组......每个字符串/接口条目也将被管理。

鉴于上述内容有更多可能的排列,始终确保Dispose()知道初始New()分配中使用的类型更安全。

非托管类型

也许在上面的讨论中,我应该对 托管类型的类型进行特定限定。当包含结构为Dispose() d时,可以推断(并且准确)非托管类型自动解除分配。

E.g。如果您的记录结构包含对象引用,那么因为对象引用不是托管类型,您仍然必须显式控制该对象实例的生命周期。 (幸运的是,有很多技术可以这样做。)