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