2

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

我想将地址存储到 aTList但我有点不安全,因为它使用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.

既然我没有将它放在堆栈中,那会安全吗?

4

1 回答 1

5

Delphi 中的动态内存分配将返回而不是堆栈上的内存。(请参阅Marco Cantu 对这些术语的解释。)因此,如果对该内存的引用可用,则该内存将在您的应用程序中“全局访问” 。该New()过程是动态分配内存的一种方式。(其他一些是:GetMem()、字符串分配、对象创建。)

因此,您提出的建议是安全的(就不会导致访问冲突而言)。
但是,它会泄漏内存,因为您使用它的方式Dispose()不会释放所有内存。

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

修复内存泄漏

您需要做的是将返回的指针MyTList.Items[i]转换为正确的类型。但首先您需要为指向您的结构的指针显式声明一个类型。作为一个标准约定,大多数 Delphi 程序员会用相同的名称声明指针类型,将 T 前缀替换为 PIe

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

然后,当您执行以下操作时:Dispose(PMyStruct(MyTList.Items[i]))编译器“识别”指针所指的类型,并将使用该信息对其托管类型执行附加操作。关键是编译器可以自动正确处理托管类型——但前提是你给它有关包含它们的结构的正确信息。

受此影响的类型有:

  • 字符串
  • 动态数组
  • 接口引用(需要释放 ref)
  • 对上述托管类型的任何间接引用也将被管理。例如
  • 如果一个VariantOleVariant引用和接口,它将被管理。
  • 如果记录(结构)中的子记录引用托管类型,它们将被托管。
  • 字符串数组,接口数组......每个字符串/接口条目也将被管理。

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

非托管类型

也许考虑到上述讨论,我应该对非托管类型的类型做出具体限定。可以推断(这是准确的)当包含结构为d时,非托管类型不会自动释放。Dispose()

E.g. If your record structure contains an object reference, then because an object reference is not a managed type, you would still have to explicitly control the lifetime of that object instance. (Fortunately there are many techniques to do so.)

于 2013-09-12T06:41:40.773 回答