Link to home
Start Free TrialLog in
Avatar of Stuart Cox
Stuart CoxFlag for Canada

asked on

How to properly Free class instances created as actual parameters to methods in Delphi

I am having some difficulty in understanding how to free up instances of objects created on the fly.
I am translating some java/c# code into Delphi.  The c# is itself a translation of the original java.  So, I have two source materials from which to compare my efforts.
I understand that both these languages feature garbage collectors to free the writer from having to explicitly manage his instance's life span.  So, there's no direct guide from which to see when their instances are freed.
The code is an implementation of the DoubleDouble type with methods that manipulate the type's value by arithmetic and functional means.  Several creators exist to allow creation of an instance of the DD value from a double, another DD or by parsing a string representation.  Arithmetic methods permit the chaining of operations together in a sort of pre/post/infix blend that resembles the mathematical expression that's being calculated.  These arithmetic, and some unshown mathematical function methods, call the class' creators as they need.
An example:
type
  TDD = class
    constructor Create( v : double );
    constructor Create( v : TDD );
    destructor Destroy;
    function Add( yhi : double; ylo : double ): TDD;
    function selfAdd( yhi : double; ylo : double ): TDD;
    function selfDivide( y : double ): TDD;
    function selfDivide( yhi : double; ylo : double ): TDD;
  end;
implementation
  function TDD.Add( y : double ): TDD;
  begin
    Result := TDD.Create( Self );
    Result.selfAdd( y );
  end;
  function TDD.selfAdd( y : TDD ): TDD;
  begin
    Result.selfAdd( y.hi, y.lo );
  end;
  function TDD.selfAdd( yhi : double; ylo : double ): TDD;
  begin
    < code to calculate DoubleDouble addition of self by yhi and ylo >
    Result := self;
  end;
  function TDD.selfDivide( y : double ): TDD;
  begin
    Result := selfDivide( v, 0.0 );
  end;
  function TDD.selfDivide( yhi : double; ylo : double ): TDD;
  begin
    < code to calculate DoubleDouble division of self by yhi and ylo >
    Result := self;
  end;
var
  c : TDD;
begin
  c := TDD.Create( 1 ).selfDivide( TDD.Create( 9 ).add( 3 ));
  writeln( c );
  c.Free;
end.

Open in new window

The issue is that this code leaks memory because implicit TDD instances are created within the expression and I can't figure out where and how to free them up.  Getting a hold of the reference of each to free them eludes me.
Sorry if this is common knowledge and I'm asking it again, but, nothing I google addresses this in a way that I can see to solve my problem.
Avatar of SteveBay
SteveBay
Flag of United States of America image

I see a number of errors or typos in this code so those need to be resolved....

Some general guidelines as far as object creation and freeing:
If you create an object in a procedure try to free that object in the same procedure
begin
   c := TDD.create(3)
   // use c
   c.free;
end;

Open in new window


If you create an object in a constructor then free the object in the destructor.
constructor TDD.Create(v: TDD);
begin
     fDD := TDD.Create(v);
end;

destructor TDD.Destroy;
begin
     fDD.free;
end;

Open in new window


I also recommend you avoid calling member routines on the same line as you create and object. It leads to confusion and is unnecessarily clever.  So This:
c := TDD.Create( 1 ).selfDivide( TDD.Create( 9 ).add( 3 ))

Open in new window

Becomes something like this:
d := TDD.Create( 9 )
d.add(3); 
c := TDD.Create( 1 );
c.selfDivide(d);
...
c.free;
d.free;

Open in new window

Unfortunately there are fundamental errors in your code. For example the selfDivide takes a double as a parameter yet you are passing an object reference to a TDD in. So the code example I provided is erroneous regardless.
ASKER CERTIFIED SOLUTION
Avatar of Sinisa Vuk
Sinisa Vuk
Flag of Croatia image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Stuart Cox

ASKER

@SteveBay - Thanks for your observations.  I was sloppy in typing in the portions of the TDD class that I gave in the example.  The constructors, destructors and the arithmetic methods are properly implemented in the actual code and do manage to produce the correct arithmetic results.  
You're certainly correct, the arithmetic methods are themselves constructors and return instances of TDD.  This seems quite acceptable in the java/c# and my direct translation prevents me from freeing what's created in the same method.  I've read of and see the drawbacks of failing to abide by this.
The line of code that you've noted as leading to confusion and as being unnecessarily clever is indeed the one that is the base of the difficulties.  It is a direct pascalization of the java and c# from the code I'm translating.  In my defense, the cleverness does lead to the expression being rather readable as an arithmetic analog, wouldn't you say?   Because there is considerable code to translate and these sorts of arithmetic expressions permeate the java, I'd like to retain these expressions for as long as possible to ease my translation task.  That way, I won't be introducing my own misinterpretation errors.
@Sinisa Vuk - Thank you too for pointing out the virtues of inheriting from TInterfacedObject.  I'd implemented some other classes during this translation using TInterfacedObject and interfaces of one sort or another.  I'd not appreciated these memory management aspects of the class.  Thanks for including the links to their explanations.
I'll make the change to descend from TInterfacedObject and see what happens and report back.
Bye-the-bye, the code I'm working from is the JTS and NTS topology suites.  It looked to me that you'd seen where I was coming from in your responding example code.  Thanks, here goes...
wow ... you're really mixing up some techniques

i see the use of fluid interfaces
> this means the return value of the methode function is self
in code: Result := Self;

misunderstanding of class (or object) and instance
a class is the blueprint of something
a instance is the physical embodiment of such a blueprint
sample:
  a house class: the architectural drawing
  a house instance: 1 of the actual houses built according to that architectural drawing

so this code is a wrong
function TDD.Add( y : double ): TDD;
  begin
    Result := TDD.Create( Self );

Open in new window


the variables you pass to the instance never get stored
you don't have any private variables defined in your class

basically you have an outline of the house on your architectural drawing
but all the plumbing, electrical, ... lines are missing

it would help if you provide the C# of Java class definitions

what are you trying to do ... using some math functions ?
check the Math unit ...
it holds a lot of functions to do such things

if the double type doesn't have enough precision the try and extended type
http://www.delphibasics.co.uk/Article.asp?Name=Numbers

type
  TDD = class(TInterfacedObject)
  private
    v: Extended;
  public
    constructor Create( v : Extended );
    function Add( y: Extended ): TDD;
    function Divide( y : Extended ): TDD;
    property Value: Extended read GetValue write SetValue;
  end;

implementation

function TDD.Add( y: extended  ): TDD;
begin
  Result := Self;
  v := v + y ;
end;

function TDD.Divide( y : Extended ): TDD;
begin
  Result := self;
  v := v / y;
end;

function TDD.GetValue: Extended;
begin
  Result := v;
end;

procedure TDD.SetValue(const y: Extended);
begin
  v := y;
end;

var
  c : TDD;
begin
  c := TDD.Create( 1 ).Divide( 9 ).add( 3 );
  writeln( c.Value );
end.

Open in new window


with interfaces you don't need to free
@Geert Gruwez - Thanks for your observations.  The purpose of the TDD class is to obtain greater precision without necessarily increasing the range of acceptable values.  The class calculates with 31 decimal digits of precision within the range of the double type.  The Delphi is a translation of the DD.java code available within the JTS Topology Suite at http://sourceforge.net/projects/jts-topo-suite.  Another source for comparison is the DD.cs source of NetTopologySuite within https://github.com/NetTopologySuite/NetTopologySuite.  
The result of the actual small program and support units that I (poorly) gave as an example is: "0.083333333333333333333333333333332".
Thanks Sinisa Vuk for your suggestion to base the TDD class on interfaces.  
I defined an interface descendant and placed the method definitions for its Divide, SelfDivide, negate, sqrt, sqr and etc., in it.  I then used that interface type on my TDD = class( TinterfacedObject, IDD ) statement and in the TDD method's definitions.  I changed all the formal parameter and function result types from TDD to IDD.  Once I fixed all my typos, it compiled and still passes the DUnit tests that I'd written to test every arithmetic and function method.  Additionally, FastMM4 with FullDebugMode, EnableMemoryLeakReporting, LogMemoryLeakDetailToFile and Log ErrorsToFile not longer reports even a single memory leak.  The former code generated some 43 leaks.
Pascal Analyzer complains about a few minor things but not over things related to what I've done to implement your suggestions.  
On the whole, I've learned that interfaces can be used productively and that I can continue my translation effort without leaving a big unsolved issue hanging in the wind.