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.