【问题标题】:Delphi error while returning TList返回 TList 时出现 Delphi 错误
【发布时间】:2017-08-25 09:42:05
【问题描述】:

我做了一个非常简单的应用程序,但我遇到了一个我真的无法理解的问题。看看这个基本代码:

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    test: TList<integer>;
    aList: TList<integer>;
  public
    { Public declarations }
    function testGenerics: TList<integer>;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
 test := testGenerics;
 test.Sort;
 showmessage(test[0].tostring);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
 test := TList<integer>.Create;
 aList := TList<integer>.Create;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
 aList.Free;
 test.Free;
end;

function TForm1.testGenerics: TList<integer>;
begin

  aList.Add(4);
  result := aList;

end;

end.

基本上,当表单打开时,我将创建testaList,然后当我按下按钮时,函数testGenerics 被调用。为什么会出现Invalid pointer operation错误?

我真的无法理解,因为我正在正确地创建和销毁对象(我猜)。这段代码可以正常工作:

function TForm1.testGenerics: TList<integer>;
begin

  Result := TList<integer>.Create;
  Result.Add(4);

end;

在这种情况下,我将返回 TList&lt;integer&gt; 的实例,但在上述情况下,我将返回 aList 的实例(这是一个 TList)。

如果我在第一种情况下是正确的,test := testGenerics 就像 test := aList(因为我实际上返回的是 aList)所以我将给 testaList 相同的引用。我对么?

【问题讨论】:

    标签: delphi


    【解决方案1】:

    在第一个示例中,每当您调用testGenerics() 时,您都在重新分配test 以指向aList 对象。您正在丢失在 OnCreate 事件中创建的原始 test 对象,因此它被泄露了。然后在OnDestroy事件中,当你调用test.Free时,它会崩溃,因为你已经事先释放了aList对象,所以你试图再次释放同一个对象,这是一个无效的操作。

    在第二个示例中,您仍在泄漏原始 test 对象(以及您分配和分配给 test 的每个 TList,除了最后一个),但您没有重新分配 test不再指向aList 对象,因此OnDestroy 事件中没有崩溃,因为两个变量都指向不同的对象。


    您首先要完成什么?以这种方式返回对象不是好的做法。在 1 元素列表上调用 Sort() 也没有任何意义。

    如果您尝试在一段时间内使用多个值填充test,则应将test 作为输入参数传递给testGenerics()(或者让testGenerics() 直接通过Self 访问test),根本不要使用返回值。

    无论如何,请摆脱您的 aList 私人成员,因为无论如何您都不会对它做任何事情。

    试试这个:

    unit Unit1;
    
    interface
    
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, generics.collections, Vcl.StdCtrls;
    
    type
      TForm1 = class(TForm)
        Button1: TButton;
        procedure Button1Click(Sender: TObject);
        procedure FormCreate(Sender: TObject);
        procedure FormDestroy(Sender: TObject);
      private
        { Private declarations }
        test: TList<integer>;
      public
        { Public declarations }
        procedure testGenerics(aList: TList<integer>);
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.dfm}
    
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      testGenerics(test);
      test.Sort;
      ShowMessage(test[0].tostring);
    end;
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      test := TList<integer>.Create;
    end;
    
    procedure TForm1.FormDestroy(Sender: TObject);
    begin
      test.Free;
    end;
    
    procedure TForm1.testGenerics(aList: TList<integer>);
    begin
      // FYI, a better way to exercise Sort()
      // would be to use RandomRange() instead
      // of a hard-coded number...
      aList.Add(4);
    end;
    
    end.
    

    【讨论】:

    • 谢谢雷米!要修复泄漏,我应该使用第二个示例并删除 FormCreate 中测试的创建?然后 ondestroy 里面的 test.free 就可以了,因为我没有发现
    • 这只会在您第一次调用testGenerics() 时防止泄漏,但在随后的调用中仍会泄漏,因为您在调用testGenerics() 之前没有调用test.Free
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-24
    • 1970-01-01
    • 2018-09-03
    相关资源
    最近更新 更多