我正在考虑来自Delphi的New
和Dispose
命令,并且想知道我是否可以在我的进程中的其他过程/函数/线程等中使用这些命令。
我想将地址存储到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.
那么这是安全的,因为我没有把它丢弃在堆栈中吗?
答案 0 :(得分:5)
动态内存分配将返回堆上的内存,而不是堆栈。 (请参阅这些术语的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]))
时,编译器会“识别”指针所指的类型,并将使用该信息为其托管类型执行其他操作。重点是编译器可以正确地自动处理托管类型 - 但是如果你给它提供有关包含它们的结构的正确信息,那么只能 。
受此影响的类型包括:
Variant
或OleVariant
引用和界面,则会对其进行管理。鉴于上述内容有更多可能的排列,始终确保Dispose()
知道初始New()
分配中使用的类型更安全。
也许在上面的讨论中,我应该对 不 托管类型的类型进行特定限定。当包含结构为Dispose()
d时,可以推断(并且准确)非托管类型不自动解除分配。
E.g。如果您的记录结构包含对象引用,那么因为对象引用不是托管类型,您仍然必须显式控制该对象实例的生命周期。 (幸运的是,有很多技术可以这样做。)