Smartpointers不能与Delphi中的通用TObjectlist一起很好地工作

问题描述 投票:0回答:2

我正在使用Spring4D在Delphi 10.3 Rio中测试智能指针。这是我的测试程序。我创建了一个通用的TObjectList,并想使用TObject将简单的Shared.Make(TTestObj.Create)添加到此列表中。问题在于,每当我将一个对象添加到列表中时,就会释放前一个对象。请参阅我的程序的输出。有谁知道如何解决这个问题?

program TestSmartPointer;

{$APPTYPE CONSOLE}

uses
  Spring,
  Diagnostics,
  Classes,
  SysUtils,
  System.Generics.Collections;

type
  TTestObj = class
  private
    FDescription: string;
  public
    property Description: string read FDescription write FDescription;
    destructor Destroy; override;
  end;
  TTestList = class(TObjectList<TTestObj>)
    destructor Destroy; override;
  end;

var
  LISTITEMCOUNT: integer;
  LISTCOUNT: integer;

procedure Test_SmartPointer;
begin
  Writeln('SmartPointer test started');
  var lTestList := Shared.Make(TTestList.Create)();
  lTestList.OwnsObjects := false;
  for var i := 1 to 10 do
  begin
    var lTestObj := Shared.Make(TTestObj.Create)();
//    var lTestObj := TTestObj.Create;
    lTestObj.Description := i.ToString;
    Writeln('TestObj added to Testlist with description ' + lTestObj.Description);
    lTestList.Add(lTestObj);
  end;
  Writeln('SmartPointer test finished');
end;

{ TTestObj }

destructor TTestObj.Destroy;
begin
  Writeln(format('TTestObj with description %s is destroyed', [FDescription]));
  inherited;
end;

{ TTestList }

destructor TTestList.Destroy;
begin
  Writeln('TTestList is destroyed');
  inherited;
end;

begin
  Test_SmartPointer;
  Readln;
end.

Output

delphi smart-pointers generic-list spring4d
2个回答
2
投票

问题是您的TObjectList保留原始TTestObj对象指针,而不是IShared<TTestObj>返回的Shared.Make<T>()接口。

var lTestList := Shared.Make(TTestList.Create)();中,您正在创建一个IShared<TTestList>reference to function: TTestList),该文件包装了您正在创建的TTestList对象。您正在调用()上的IShared,这将调用该函数以返回原始TTestList对象指针。在此示例中可以,因为IShared将在Test_SmartPointer()的生存期内保存在一个隐藏变量中,因此其refcount为1,从而使TTestList保持活动状态。

var lTestObj := Shared.Make(TTestObj.Create)();中,您正在执行相同的操作,这次是IShared<TTestObj>返回TTestObj对象指针。但是,当lTestObj在每次循环迭代结束时超出范围时,IShared的引用计数将减少。由于没有对该接口的进一步引用,它的refcount降为0,从而破坏了IShared后面的对象,这又破坏了其关联的TTestObj对象,使TObjectList带有悬空的TTestObj指针(但是您不会因此而崩溃,因为您不会以任何方式访问存储的TTestObj对象,甚至由于TObjectList而不会在OwnsObjects=false析构函数中访问)。

您需要更改TTestList来容纳IShared<TTestObj>元素而不是TTestObj元素(在这种情况下,您应该使用TList<T>而不是TObjectList<T>),并删除()上的发票IShared接口:

program TestSmartPointer;

{$APPTYPE CONSOLE}

uses
  Spring,
  Diagnostics,
  Classes,
  SysUtils,
  System.Generics.Collections;

type
  TTestObj = class
  private
    FDescription: string;
  public
    property Description: string read FDescription write FDescription;
    destructor Destroy; override;
  end;

  TTestList = class(TObjectList<IShared<TTestObj>>)
    destructor Destroy; override;
  end;

var
  LISTITEMCOUNT: integer;
  LISTCOUNT: integer;

procedure Test_SmartPointer;
begin
  Writeln('SmartPointer test started');
  var lTestList := Shared.Make(TTestList.Create);
  for var i := 1 to 10 do
  begin
    var lTestObj := Shared.Make(TTestObj.Create);
    lTestObj.Description := i.ToString;
    Writeln('TestObj added to Testlist with description ' + lTestObj.Description);
    lTestList.Add(lTestObj);
  end;
  Writeln('SmartPointer test finished');
end;

{ TTestObj }

destructor TTestObj.Destroy;
begin
  Writeln(Format('TTestObj with description %s is destroyed', [FDescription]));
  inherited;
end;

{ TTestList }

destructor TTestList.Destroy;
begin
  Writeln('TTestList is destroyed');
  inherited;
end;

begin
  Test_SmartPointer;
  Readln;
end.

0
投票

这里是有效的代码(感谢Remy Lebeau)。由于Delphi没有垃圾收集器,并且ARC已删除,因此我一直在寻找一种自动释放对象的常规结构。我对smartpointers的印象是,它有点复杂,无法用作自动释放对象的易于使用的通用结构。

program TestSmartPointer;

{$APPTYPE CONSOLE}

uses
  Spring,
  Diagnostics,
  Classes,
  SysUtils,
  System.Generics.Collections;

type
  TTestObj = class
  private
    FDescription: string;
  public
    property Description: string read FDescription write FDescription;
    destructor Destroy; override;
  end;
  TTestList = class(TList<IShared<TTestObj>>)
  public
    destructor Destroy; override;
  end;

procedure Test_SmartPointer;
var
  lTestList: IShared<TTestList>;
  lTestObj: IShared<TTestObj>;
  i: integer;
begin
  Writeln('SmartPointer test started');
  lTestList := Shared.Make(TTestList.Create);
  for i := 1 to 10 do
  begin
    lTestObj := Shared.Make(TTestObj.Create);
    lTestObj.Description := i.ToString;
    Writeln(format('TestObj with description %s added to Testlist', [lTestObj.Description]));
    lTestList.Add(lTestObj);
  end;
  for lTestObj in lTestList do
  begin
    writeln(lTestObj.Description);
  end;

  Writeln('SmartPointer test finished');
end;

{ TTestObj }

destructor TTestObj.Destroy;
begin
  Writeln(format('TestObj with description %s is destroyed', [FDescription]));
  inherited;
end;

{ TTestList }

destructor TTestList.Destroy;
begin
  Writeln('TTestList is destroyed');
  inherited;
end;

begin
  Test_SmartPointer;
  Readln;
end.

Output

© www.soinside.com 2019 - 2024. All rights reserved.