问题描述
我有一个智能指针的实现,我试图在一个通用的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
完成
我不认为我非常喜欢 I have an implementation of smart pointers, and I've tried to implement it on a generic TList. The problem is that I can not access the elements from the list and I have no clue where the issue is. The You can observe this happening by putting a breakpoint inside 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 with That's because you have started managing the Consider this variant of your program: Output I don't think I like this idea of 这篇关于Delphi - 智能指针和泛型TList的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! ISmartPointer< TList< ISmartPointer< TTest>>>< / code>的想法。坦率地说,我从来没有相信Delphi中智能指针的有效性。
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
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.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.ISmartPointer<TList<TTest>>
ISmartPointer<TList<ISmartPointer<TTest>>>
TTest
instance lifetimes by wrapping with a smart point. Once you've started doing that, you've got to do so consistently.{$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
finished
ISmartPointer<TList<ISmartPointer<TTest>>>
very much. Honestly, I've never been convinced by the effectiveness of smart pointers in Delphi.