Delphi - 智能指针和泛型TList

3
我有一个智能指针的实现,并尝试将其实现在通用的TList上。
program Project2;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  System.Generics.Collections;


type
  ISmartPointer<T> = reference to function: T;

  TSmartPointer<T: class, constructor> = class(TInterfacedObject, ISmartPointer<T>)
  private
    FValue: T;
    FName: string;
  public
    constructor Create; overload;
    constructor Create(AValue: T); overload;
    destructor Destroy; override;
    function Invoke: T;

    property Name: string read FName write FName;
  end;

  TTest = class
    FString: String;
  public
   property MyStrign: String read FString write FString;
  end;

{ TSmartPointer<T> }

constructor TSmartPointer<T>.Create;
begin
  inherited;

  FValue := T.Create;
end;

constructor TSmartPointer<T>.Create(AValue: T);
begin
  inherited Create;
  if AValue = nil then
    FValue := T.Create
  else
    FValue := AValue;
end;

destructor TSmartPointer<T>.Destroy;
begin
  if Assigned(FValue) then
    FValue.Free;

  inherited;
end;

function TSmartPointer<T>.Invoke: T;
begin
  Result := FValue;
end;

function TestSMP():ISmartPointer<TList<TTest>>;
var lTTest: ISmartPointer<TTest>;
    i: Integer;
begin
 Result := TSmartPointer<TList<TTest>>.Create();

 for I := 0 to 5 do
  begin
    lTTest := TSmartPointer<TTest>.Create();
    lTTest.FString := IntToStr(i);

    Result().Add(lTTest);
  end;
end;

var Testlist:ISmartPointer<TList<TTest>>;
    i: Integer;

begin
  try
   Testlist := TestSMP();

   for I := 0 to 5 do
    Writeln(Testlist[i].FString);
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
 Writeln('finished');
 Readln;
end.

问题是,我无法访问列表中的元素,也不知道问题出在哪里。
1个回答

4
function TestSMP(): ISmartPointer<TList<TTest>>;
var 
   lTTest: ISmartPointer<TTest>;
   i: Integer;
begin
   Result := TSmartPointer<TList<TTest>>.Create();

   for I := 0 to 5 do
   begin
      lTTest := TSmartPointer<TTest>.Create();
      lTTest.FString := IntToStr(i);

      Result().Add(lTTest);
   end;
end;
lTTest接口变量是唯一维持TTest实例存在的东西。每次循环时,当你赋值给lTTest时,上一个TTest实例就被销毁了。函数退出时,包含'5'的最后一个TTest实例也被销毁了。你所创建的所有实例都已经死亡。
你可以通过在TSmartPointer<T>.Destroy中放置断点并查看调用栈来观察这种情况发生。其中一个后果是,在TTest实例被销毁后,你的代码实际上仍在引用它们。虽然我们都没有观察到运行时错误,但这显然是错误的做法。
关键点在于,一旦开始使用智能指针管理生命周期,就必须全部使用。在此一举,全走一遍。这基本上迫使你替换掉原有的方式。
ISmartPointer<TList<TTest>>

使用

ISmartPointer<TList<ISmartPointer<TTest>>>

那是因为您已经开始通过智能指针包装来管理TTest实例的生命周期。一旦开始这样做,就必须始终如一地这样做。
考虑您程序的这个变体:
{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.Generics.Collections;

type
  ISmartPointer<T> = reference to function: T;

  TSmartPointer<T: class, constructor> = class(TInterfacedObject,
    ISmartPointer<T>)
  private
    FValue: T;
  public
    constructor Create;
    destructor Destroy; override;
    function Invoke: T;
  end;

  TTest = class
    FString: string;
  end;

constructor TSmartPointer<T>.Create;
begin
  inherited;
  FValue := T.Create;
end;

destructor TSmartPointer<T>.Destroy;
begin
  FValue.Free;
  inherited;
end;

function TSmartPointer<T>.Invoke: T;
begin
  Result := FValue;
end;

function TestSMP(): ISmartPointer<TList<ISmartPointer<TTest>>>;
var
  lTTest: ISmartPointer<TTest>;
  i: Integer;
begin
  Result := TSmartPointer<TList<ISmartPointer<TTest>>>.Create();

  for i := 0 to 5 do
  begin
    lTTest := TSmartPointer<TTest>.Create();
    lTTest.FString := IntToStr(i);
    Result().Add(lTTest);
  end;
end;

var
  i: Integer;
  Testlist: ISmartPointer<TList<ISmartPointer<TTest>>>;

begin
  Testlist := TestSMP();
  for i := 0 to 5 do
    Writeln(Testlist[i]().FString);
  Writeln('finished');
  Readln;
end.

输出

0
1
2
3
4
5
完成

我不太喜欢 ISmartPointer<TList<ISmartPointer<TTest>>> 这个想法。说实话,我一直对 Delphi 中智能指针的有效性持怀疑态度。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接