Knowledge Base

Smartpointers in Delphi

As you know, Delphi has no garbage collector. ARC will soon be removed from the non-windows compiler. So Delphi has no structure for automatically freeing objects. We carried out some research to see whether Smartpointers are suitable for automatic freeing of objects. The concept of Smartpointers is borrowed from C++ and was first introduced in Delphi by Barry Kelly. The Spring4D framework has also an implementation of Smartpointers.

To show an example of the use of Smartpointers we’ll have a look at some code:

var
  s: TStringList;
begin
  s := TStringList.Create;
try
  DoSomething(s);
finally
  s.Free;
end;

With Smartpointers the code will look like this:

var
  s: IShared<TStringlist>;
begin
  s := Shared.Make(TStringList.Create);
  DoSomething(s);
end;

We wrote a testprogram to test a more complex scenario. Here is the code of the testprogram:

program TestSmartPointer;

{$APPTYPE CONSOLE}

uses
  Spring,
  Diagnostics,
  Classes,
  SysUtils,
  System.Generics.Collections;

type
  TTestObj = class
  private
    FDescription: string;
  public
    property Description: string read FDescription write FDescription;
    destructor Destroy; override;
  end;

  TTestList = class(TList<IShared<TTestObj>>)
  public
    destructor Destroy; override;
  end;

procedure Test_SmartPointer;
var
  lTestList: IShared<TTestList>;
  lTestObj: IShared<TTestObj>;
  i: integer;
begin
  writeln('SmartPointer test started');
  lTestList := Shared.Make(TTestList.Create);
  for i := 1 to 10 do
  begin
    lTestObj := Shared.Make(TTestObj.Create);
    lTestObj.Description := i.ToString;
    writeln(format('TestObj with description %s added to Testlist', [lTestObj.Description]));
    lTestList.Add(lTestObj);
  end;
  
  for lTestObj in lTestList do
    writeln(format('Item %d of Testlist has value %s', lTestList.IndexOf(lTestObj), lTestObj.Description));

  writeln('SmartPointer test finished');
end;

{ TTestObj }

destructor TTestObj.Destroy;
begin
  writeln(format('TestObj with description %s is destroyed', [FDescription]));
  inherited;
end;

{ TTestList }

destructor TTestList.Destroy;
begin
  writeln('TTestList is destroyed');
  inherited;
end;

begin
  Test_SmartPointer;
  readln;
end.

In this program a generic List TTestList is populated with TTestObj objects. Notice the declaration of the TTestList:

TTestList = class(TList<IShared<TTestObj>>)

The output of this testprogram is in the picture below.

As you can see the objects are freed after the ‘SmartPointer test finished’ line. So when the Test_SmartPointer procedure is finished the local variables go out of scope and are automatically freed. This works fine.
We also tested the performance and the memory used. The results are as follow:

In the speedtest 100.000 lists were created and each list was populated with 100 items. The smartpointers are 29,17% slower than when the classic technique is used.
In the memorytest 1 list was created with 100.000 items. When the list was fully populated the memory used by the program was measured. The smartpointers use 26,94% more memory than when the classic technique is used. This are considerable differences.

Conclusion

Smartpointers work technically fine. The objects are freed as expected. However, there is a considerable disadvantage when it comes to speed and used memory. Besides this, every object has to be declared with IShared<object> and created with Shared.Make(object.create). Our recommendation of smartpointers is that it’s a bit too much overhead in performance and code to be used as an easy-to-use general stucture for automatically freeing objects in Delphi.

References:

https://blog.marcocantu.com/blog/2018-october-Delphi-ARC-directions.html

http://blog.barrkel.com/2008/09/smart-pointers-in-delphi.html

https://delphisorcery.blogspot.com/2015/01/smart-pointers-in-delphi.html

https://en.delphipraxis.net/topic/402-smart-pointers-generics-vrs-non-generic-implementastion/

 

Contact

Let us help you to realise your ambitions