0

我正在使用 Spring4D 在 Delphi 10.3 Rio 中测试智能指针。这是我的测试程序。我创建了一个泛型TObjectList,我想使用简单TObject的 s 添加到这个列表中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.

输出

4

2 回答 2

3

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

var lTestList := Shared.Make(TTestList.Create)();中,您正在创建一个IShared<TTestList>(a reference to function: TTestList) 来包装TTestList您正在创建的对象。您正在调用()IShared它调用函数以返回原始TTestList对象指针。在这个例子中是可以的,因为IShared将在 的生命周期内被保存在一个隐藏变量中Test_SmartPointer(),因此它的 refcount 为 1,保持TTestList活动状态。

var lTestObj := Shared.Make(TTestObj.Create)();你做同样的事情,这次是为了IShared<TTestObj>返回一个TTestObj对象指针。但是,当lTestObj每次循环迭代结束时超出范围时, 的引用计数IShared会减少。由于没有对该接口的进一步引用,它的引用计数下降到 0,破坏了 后面的对象IShared,这反过来又破坏了它的关联TTestObj对象,留下TObjectList一个悬空TTestObj指针(但你不会遇到任何崩溃,因为你没有以任何方式访问存储的TTestObj对象,即使在TObjectList析构函数中也是如此OwnsObjects=false)。

您需要更改TTestList为保存IShared<TTestObj>元素而不是TTestObj元素(在这种情况下,您应该使用TList<T>代替),并在调用时TObjectList<T>摆脱接口上的调用:()ISharedShared.Make()

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.
于 2020-05-07T19:12:23.520 回答
0

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

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.

输出

于 2020-05-07T22:25:10.117 回答