Delphi Programming

  1. Home
  2. Computing & Technology
  3. Delphi Programming

Association and Composition - Delphi OOP Part 9 - Chapter 20

By Zarko Gajic, About.com

Ex 9.2 step 5 Freeing a composed class

There is still a problem with this program as it stands. Can you see what it is?

Clicking the Create Sale button frees the ThisSale object, but what about ThisSale’s constituent TCustomer class (FCustomer)? This is not destroyed and its only reference is through ThisSale. So when ThisSale is freed, the memory referred to by FCustomer is still allocated in memory. But now it becomes inaccessible and we have memory leakage. So, just as the Create must be propagated to all constituent objects, so too must the Destroy.

unit SaleU;

interface

uses CustomerU;

type
   TSale = class(TObject)
   private
    FCustomer: TCustomer;
    FAmount: string;
   public
    property Name: string read GetName;
    property Phone: string read GetPhone;
    property Amount: string read FAmount;
    constructor Create (ACustomer: TCustomer; AnAmount: string) ;
    destructor Destroy; override;
   end;

implementation

// TSale.Create, TSale.GetName, TSale.GetPhone as before

destructor TSale.Destroy;
begin
  FCustomer.Free;
  inherited;
end;

end.

When an object is freed, Delphi checks to see whether the object has been assigned, ie whether or not it exists. If it does, Delphi calls the object’s destructor method, which by convention is called Destroy.

If the object does not have a defined destructor it calls the inherited destructor. Because TSale is a composed class, it needs a destructor so that it can propagate the object destruction to its constituents: a composed class has the responsibility first to free each of its constituents (eg line 35 above) before allowing itself to be destroyed through the inherited destructor (line 36 above). If any of the constituent objects are also composed objects they too will have the responsibility for freeing their constituents before allowing themselves to be destroyed.

Notice the difference in how a constructor and a destructor use the inherited keyword.

The first action in the constructor is to call the inherited constructor: the object must exist before it can propagate the construction of the constituent objects and/or initialise its data fields. However, with a destructor, it must first propagate all the necessary destruction while it exists (eg line 35) before allowing its own destruction through the inherited Destroy.

Something that might be a little puzzling is that the inherited call in this constructor is followed by the inherited constructor’s name, Create (step 3, line 20), while the destructor uses only the inherited keyword (line 36) without the inherited method’s name.

The inherited keyword on its own calls the parent’s method with the same signature, which is the case with the destructor. However, the subclass’s Create has two parameters (line 18) while its parent’s has no parameters, and so the inherited call must identify which of the parent’s methods is being called and pass the parameters.

In summary, there is a symmetry between the constructor and the destructor. In the constructor we first create the composed class and then all its constituents. In the destructor we reverse this order. First we Free all the constituents and then finally destroy the composed class.

Propagation

With inheritance we often rely on the inherited Create and Destroy. With composition we must explicitly Create and Destroy the constituent objects. This is an example of propagation. For any action that can be carried out on the composed object, it becomes the composed class’s responsibility to propagate this action to its constituents. If the composed object can be created (or destroyed or copied or ...), it is responsible for creating its constituent objects, and so on.

The concept of propagation, that whatever is done to the composed class the composed class must then do to its constituents, is important in composition. Propagating the creation of the constituents is needed to provide encapsulation (ie it prevents encapsulation leakage). Propagating destruction frees otherwise inaccessible memory being held by the constituents and so avoids memory leakage. Including a constructor, a destructor and a CopyFrom method simplifies propagation and reduces the effect of future changes.

When we create a RAD VCL object at runtime we assign it to an owner by passing a suitable parameter in the constructor. If this parameter is another component (typically Self), that component takes responsibility for propagating the destruction. If we set the owner parameter to nil, we have the responsibility of destroying the RAD object. With association, the associated object already exists and has an independent lifetime, and so we are typically not concerned with either its creation or destruction.

This ends chapter 20. In the next chapter, we will talk about "Deep copies and the Assign method".

Zarko Gajic
Guide since 1998

Zarko Gajic
Delphi Programming Guide

Explore Delphi Programming

About.com Special Features

Delphi Programming

  1. Home
  2. Computing & Technology
  3. Delphi Programming
  4. Coding Delphi Applications
  5. OOP in Delphi
  6. Free Online OOP Course
  7. Association and Composition - Delphi OOP Part 9 - Chapter 20

©2009 About.com, a part of The New York Times Company.

All rights reserved.