我有一个智能指针的实现,并且我试图在一个通用的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.


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

最佳答案

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实例都会被破坏。当函数退出时,最终的TTest实例(包含'5'的实例)被销毁。您精心创建的所有实例现在都已消失。

您可以通过在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中智能指针的有效性。

关于delphi - Delphi-智能指针和泛型TList,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/28968208/

10-09 09:29