Delphi - 智能指针和泛型TList [英] Delphi - smart pointers and generics TList

查看:318
本文介绍了Delphi - 智能指针和泛型TList的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个智能指针的实现,我试图在一个通用的TList上实现它。

  program Project2; 

{$ APPTYPE CONSOLE}

{$ R * .res}

使用
System.SysUtils,
系统.Generics.Collections;


类型
ISmartPointer< T> =对函数的引用:T;

TSmartPointer< T:class,constructor> = class(TInterfacedObject,ISmartPointer< T>)
private
FValue:T;
FName:string;
public
构造函数创建;超载;
构造函数创建(AValue:T);超载;
析构函数Destroy;覆盖;
函数调用:T;

属性名称:字符串读取FName写入FName;
end;

TTest = class
FString:String;
public
属性MyStrign:字符串读取FString写入FString;
end;

{TSmartPointer< T> }

构造函数TSmartPointer< T> .Create;
begin
继承;

FValue:= T.Create;
end;

构造函数TSmartPointer< T> ;.Create(AValue:T);
begin
继承创建;
如果AValue = nil然后
FValue:= T.Create
else
FValue:= AValue;
end;

析构函数TSmartPointer< T> ;.Destroy;
开始
如果分配(FValue)然后
FValue.Free;

继承;
end;

函数TSmartPointer< T> .Invoke:T;
begin
结果:= FValue;
end;

函数TestSMP():ISmartPointer< TList< TTest>>;
var lTTest:ISmartPointer< TTest>;
我:整数;
begin
结果:= 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>>;
我:整数;

begin
try
Testlist:= TestSMP();

for I:= 0 to 5 do
Writeln(Testlist [i] .FString);
除了
在E:例外
Writeln(E.ClassName,':',E.Message);
end;
Writeln('已完成');
Readln;
结束。

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

解决方案

function TestSMP():ISmartPointer< TList< TTest>>;
var
lTTest:ISmartPointer< TTest>;
我:整数;
begin
结果:= 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 中放置一个断点来观察这种情况的发生。 c $ c>并查看调用堆栈。其中一个后果就是你的代码实际上是在它们被销毁后引用 TTest 实例。偶然,您和我都没有观察到运行时错误,但这样做显然是错误的。



关键在于,一旦您开始使用智能管理生命周期指针,你必须独占。一分钱,一英镑。这几乎推动你取代

  ISmartPointer< TList< TTest>> 

with

  ISmartPointer<&的TList LT; ISmartPointer< t检验>>> 

这是因为您已经开始管理 TTest 实例生命周期通过包装一个智能点。一旦你开始这样做,你必须一直这样做。



考虑你的程序的这种变体:

  {$ APPTYPE CONSOLE} 

使用
System.SysUtils,
System.Generics.Collections;

类型
ISmartPointer< T> =对函数的引用:T;

TSmartPointer< T:class,constructor> = class(TInterfacedObject,
ISmartPointer< T>)
private
FValue:T;
public
构造函数创建;
析构函数Destroy;覆盖;
函数调用:T;
end;

TTest = class
FString:string;
end;

构造函数TSmartPointer< T> .Create;
begin
继承;
FValue:= T.Create;
end;

析构函数TSmartPointer< T> ;.Destroy;
begin
FValue.Free;
继承;
end;

函数TSmartPointer< T> .Invoke:T;
begin
结果:= FValue;
end;

函数TestSMP():ISmartPointer< TList< ISmartPointer< TTest>>>
var
lTTest:ISmartPointer< TTest>;
我:整数;
begin
结果:= 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:整数;
Testlist:ISmartPointer< TList< ISmartPointer< TTest>>>

begin
Testlist:= TestSMP();
for i:= 0 to 5 do
Writeln(Testlist [i]()。FString);
Writeln('已完成');
Readln;
结束。

输出

 
0
1
2
3
4
5
完成

我不认为我非常喜欢 ISmartPointer< TList< ISmartPointer< TTest>>>< / code>的想法。坦率地说,我从来没有相信Delphi中智能指针的有效性。

I have an implementation of smart pointers, and I've tried to implement it on a generic 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.

The problem is that I can not access the elements from the list and I have no clue where the issue is.

解决方案

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;

The lTTest interface variable is the only thing that is keeping the TTest instances alive. Each time around the loop, when you assign to lTTest, the previous TTest instance is destroyed. When the function exits, the final TTest instance, the one containing '5' is destroyed. All the instances you lovingly created are now dead.

You can observe this happening by putting a breakpoint inside TSmartPointer<T>.Destroy and looking at the call stack. One of the consequences of this is that your code is actually referring to the TTest instances after they have been destroyed. By chance, neither you nor I are observing runtime errors, although it is clearly wrong to do this.

The key point here is that once you start managing the lifetime using smart pointers, you have to do so exclusively. In for a penny, in for a pound. Which pretty much pushes you to replace

ISmartPointer<TList<TTest>>

with

ISmartPointer<TList<ISmartPointer<TTest>>>

That's because you have started managing the TTest instance lifetimes by wrapping with a smart point. Once you've started doing that, you've got to do so consistently.

Consider this variant of your program:

{$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.

Output

0
1
2
3
4
5
finished

I don't think I like this idea of ISmartPointer<TList<ISmartPointer<TTest>>> very much. Honestly, I've never been convinced by the effectiveness of smart pointers in Delphi.

这篇关于Delphi - 智能指针和泛型TList的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆