我有一个智能指针的实现,我已经尝试在通用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.
问题是我无法访问列表中的元素,我不知道问题出在哪里。
答案 0 :(得分: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
实例都会被销毁。当函数退出时,最后的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 finished
我认为我不太喜欢ISmartPointer<TList<ISmartPointer<TTest>>>
的这个想法。老实说,我从未对Delphi中智能指针的有效性深信不疑。