Delphi的New分配的内存是否可以被Dispose全局访问? [英] Is memory allocated by Delphi's `New` globally accessible for `Dispose`?

查看:122
本文介绍了Delphi的New分配的内存是否可以被Dispose全局访问?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在考虑Delphi中的 New Dispose 命令,并且想知道是否可以在其他过程/函数/线程等。



我要将地址存储到 TList 但我有点不安全,因为它使用了 var 引用,该引用可用于保存 vars的实际地址。
这是我的代码:

  unit Unit1; 

界面

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

类型
TForm1 = class(TForm)
Button1:TButton;
Button2:TButton;
过程FormCreate(Sender:TObject);
过程Button1Click(Sender:TObject);
过程Button2Click(Sender:TObject);
private
MyTList:TList;
public
{公开声明}
结尾;

var
Form1:TForm1;

类型
TMyStruct =记录
Int1:整数;
Int2:整数;
Str1:字符串;
Str2:字符串;
结尾;


实现

{$ R * .dfm}

过程TForm1.Button1Click(Sender:TObject);
var
P_TMyStruct:^ TMyStruct;
I:整数;
for I开始
:= 1到3开始
新(P_TMyStruct);
P_TMyStruct ^ .Int1:= I;
P_TMyStruct ^ .Int2:= 1337;
P_TMyStruct ^ .Str1:= inttostr(I);
P_TMyStruct ^ .Str2:=‘1337’;
MyTList.Add(P_TMyStruct);
结尾;
结尾;

过程TForm1.Button2Click(Sender:TObject);
var
I:整数;
for I:= 0 to MyTList.Count-1开始
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]);
结尾;
结尾;

过程TForm1.FormCreate(Sender:TObject);
开始
MyTList:= TList.Create;
结尾;

结尾。

那是安全的,因为我没有将其丢弃在堆栈中吗?

解决方案

动态 Delphi中的内存分配将返回 heap 上的内存,而不返回堆栈。 (请参见 Marco Cantu对这些术语的解释。)因此,记忆将是全球可访问的您的应用程序中 提供了对该内存的引用 New()过程是动态分配内存的一种方法。 (其他一些是: GetMem(),字符串分配,对象创建。)


所以您建议的是安全的(在

但是,它将泄漏内存,因为您使用它的方式 Dispose()不会释放所有内存内存。


将字符串值分配给该结构时,堆上会分配更多的内存。当您以后 Dispose 您的结构时,在 New()中分配的确切内存将被释放,但是在较低级别, (对内存的简单Pointer引用),Delphi不知道可能还有其他内部结构需要释放。


修复内存泄漏


您需要做的是转换由 MyTList返回的指针.Items [i] 更改为正确的类型。但是首先,您需要显式声明指向结构体的指针的类型。按照标准惯例,大多数Delphi程序员都会用相同的名称声明指针类型,用T代替T前缀。即


TMyStruct =记录
Int1:整数;
Int2:整数;
Str1:字符串;
Str2:字符串;
结尾;

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


受影响的类型



  • strings

  • 动态数组

  • 接口引用(

  • 还可以管理对上述托管类型的任何间接引用。例如,

  • 如果 Variant OleVariant 引用和接口,它将是

  • 如果记录(结构)中的子记录引用托管类型,则将对其进行管理。

  • 字符串数组,接口数组...每个字符串/接口条目也将得到管理。


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


非托管类型


也许经过以上讨论,我应该对 not 托管类型。当包含结构为 Dispose() d时,可以推断出(很准确)非托管类型是 not 自动取消分配的。

例如如果您的记录结构包含对象引用,则由于对象引用不是托管类型,因此您仍然必须显式控制该对象实例的生存期。 (幸运的是,有很多技术可以做到这一点。)


I am thinking about the New and Dispose commands from Delphi and was wondering if I can use these commands in other procedures/functions/threads, etc. within my process.

I would like to store the address to a TList but I am a little insecure since it uses the var reference which could be used to 'Save' the vars actual address. I don't want any access violations or anything... Here is my code:

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.

So would that be safe since I'm not disposing it in the stack?

解决方案

Dynamic memory allocations in Delphi will return memory on the heap and not the stack. (See Marco Cantu's explanation of these terms.) As such, the memory will be "globally accessible" in your application provided a reference to that memory is available. The New() procedure is one way to dynamically allocate memory. (Some others are: GetMem(), string assignments, object creation.)

So what you're proposing is safe (in terms of not causing access violations).
However, it will leak memory because the way you are using it, Dispose() will not deallocate all memory.

When you assign a string value to the struct, more memory is allocated on the heap. When you later Dispose your struct, the exact memory allocated in New() is deallocated, but at the low level, (simple Pointer reference to the memory), Delphi has no knowledge that there may be other internal structures that need deallocation; so the strings are leaked.

Fixing the memory leak

What you need to do is cast the pointer returned by MyTList.Items[i] to its correct type. But first you need to explicitly declare a type for the pointer to your struct. As a standard convention, most Delphi programmers will declare the pointer type with the same name, replacing the T prefix with P. I.e.

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

Then when you do the following: Dispose(PMyStruct(MyTList.Items[i])), the compiler "recongnises" the type the pointer is referring to and will use that information to perform additional actions for its managed types. The point is the compiler can automatically handle the managed types correctly - but only if you give it the correct information about the struct that contains them.

The types affected by this are:

  • strings
  • dynamic arrays
  • interface references (which will need a ref released)
  • Also any indirect references to the above managed types will be managed. E.g.
  • If a Variant or OleVariant references and interface, it will be managed.
  • If a child record within a record (struct) references managed types, they will be managed.
  • arrays of strings, arrays of interfaces... each string/interface entry will also be managed.

Given that there are many more possible permutations to the above, it is safer to always ensure that Dispose() knows the type that was used in the initial New() allocation.

Non Managed Types

Perhaps given the above discussion, I should make a specific qualification about types that are not managed types. One can infer (and it would be accurate) that non managed types are not automatically deallocated when the containing structure is Dispose()d.

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.)

这篇关于Delphi的New分配的内存是否可以被Dispose全局访问?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆