Constructors and Destructors

Top  Previous  Next

Constructors and Destructors are are supported as normal (a part of TObject). It must be underlined that when writing components (derived from TW3CustomControl, TW3GraphicControl or TW3Component) you rarely override these directly like under Delphi or free pascal, instead you override the protected methods InitializeObject and FinalizeObject. This is to compensate for the lack of BeforeDestruction and AfterConstruction in our object model.

Constructors

A constructor is a special method that creates and initializes instance objects. The declaration of a constructor looks like a procedure declaration, but it begins with the word constructor. Examples:

 constructor Create;

 constructor Create(AOwner: TComponent);

 

Constructors must use the default register calling convention. Although the declaration specifies no return value, a constructor returns a reference to the object it creates or is called in.

A class can have more than one constructor, but most have only one. It is conventional to call the constructor Create.

To create an object, call the constructor method on a class type. For example:

 MyObject := TMyClass.Create;

This allocates storage for the new object, sets the values of all ordinal fields to zero, assigns nil to all pointer and class-type fields, and makes all string fields empty. Other actions specified in the constructor implementation are performed next; typically, objects are initialized based on values passed as parameters to the constructor. Finally, the constructor returns a reference to the newly allocated and initialized object. The type of the returned value is the same as the class type specified in the constructor call.

If an exception is raised during the execution of a constructor that was invoked on a class reference, the Destroy destructor is automatically called to destroy the unfinished object.

When a constructor is called using an object reference (rather than a class reference), it does not create an object. Instead, the constructor operates on the specified object, executing only the statements in the constructor's implementation, and then returns a reference to the object. A constructor is typically invoked on an object reference in conjunction with the reserved word inherited to execute an inherited constructor.

Here is an example of a class type and its constructor:

   type

    TShape = class(TGraphicControl)

      private

        FPen: TPen;

        FBrush: TBrush;

        procedure PenChanged(Sender: TObject);

        procedure BrushChanged(Sender: TObject);

      public

        constructor Create(Owner: TComponent); override;

        destructor Destroy; override;

        ...

    end;

 

 constructor TShape.Create(Owner: TComponent);

 begin

     inherited Create(Owner);     // Initialize inherited parts

     Width := 65;          // Change inherited properties

     Height := 65;

     FPen := TPen.Create;  // Initialize new fields

     FPen.OnChange := PenChanged;

     FBrush := TBrush.Create;

     FBrush.OnChange := BrushChanged;

 end;

 

The first action of a constructor is usually to call an inherited constructor to initialize the object's inherited fields. The constructor then initializes the fields introduced in the descendent class. Because a constructor always clears the storage it allocates for a new object, all fields start with a value of zero (ordinal types), nil (pointer and class types), empty (string types), or Unassigned (variants). Hence there is no need to initialize fields in a constructor's implementation except to nonzero or nonempty values.

When invoked through a class-type identifier, a constructor declared virtual is equivalent to a static constructor. When combined with class-reference types, however, virtual constructors allow polymorphic construction of objects--that is, construction of objects whose types are not known at compile time.

 

Destructors

A destructor is a special method that destroys the object where it is called and deallocates its memory. The declaration of a destructor looks like a procedure declaration, but it begins with the word destructor. Example:

 destructor SpecialDestructor(SaveData: Boolean);

 destructor Destroy; override;

 

Destructors on Win32 must use the default register calling convention. Although a class can have more than one destructor, it is recommended that each class override the inherited Destroy method and declare no other destructors.

To call a destructor, you must reference an instance object. For example:

 MyObject.Destroy; 

 

When a destructor is called, actions specified in the destructor implementation are performed first. Typically, these consist of destroying any embedded objects and freeing resources that were allocated by the object. Then the storage that was allocated for the object is disposed of.

Here is an example of a destructor implementation:

 destructor TShape.Destroy;

 begin

     FBrush.Free;

     FPen.Free;

     inherited Destroy;

 end;

 

The last action in a destructor's implementation is typically to call the inherited destructor to destroy the object's inherited fields.

When an exception is raised during the creation of an object, Destroy is automatically called to dispose of the unfinished object. This means that Destroy must be prepared to dispose of partially constructed objects. Because a constructor sets the fields of a new object to zero or empty values before performing other actions, class-type and pointer-type fields in a partially constructed object are always nil. A destructor should therefore check for nil values before operating on class-type or pointer-type fields. Calling the Free method (defined in TObject) rather than Destroy offers a convenient way to check for nil values before destroying an object.

Class Constructors

A class constructor is a special class method that is not accessible to developers. Calls to class constructors are inserted automatically by the compiler into the initialization section of the unit where the class is defined. Normally, class constructors are used to initialize the static fields of the class or to perform a type of initialization, which is required before the class or any class instance can function properly. Even though the same result can be obtained by placing class initialization code into the initialization section, class constructors have the benefit of helping the compiler decide which classes should be included into the final binary file and which should be removed from it.

The next example shows the usual way of initializing class fields:

 type

   TBox = class

   private

     class var FList: TList<Integer>;

   end;

 

 implementation

 

 initialization

   { Initialize the static FList member }

   TBox.FList := TList<Integer>.Create();

 

 end.

 

This method has a big disadvantage: even though an application can include the unit in which TBox is declared, it may never actually use the TBox class. In the current example, the TBox class is included into the resulting binary, because it is referenced in the initialization section. To alleviate this problem, consider using class constructors:

 type

   TBox = class

   private

     class var FList: TList<Integer>;

     class constructor Create;

   end;

 

 implementation

 

 class constructor TBox.Create;

 begin

   { Initialize the static FList member }

   FList := TList<Integer>.Create();

 end;

 

 end.

 

 

In this case, the compiler checks whether TBox is actually used anywhere in the application, and if it is used, a call to the class constructor is added automatically to the initialization section of the unit.

Note: Even though the compiler takes care of ordering the initialization of classes, in some complex scenarios, ordering may become random. This happens when the class constructor of a class depends on the state of another class that, in turn, depends on the first class.

Note: The class constructor for a generic class or record may execute multiple times. The exact number of times the class constructor is executed in this case depends on the number of specialized versions of the generic type. For example, the class constructor for a specialized TList<String> class may execute multiple times in the same application.

Class Destructors

Class destructors are the opposite of class constructors in that they perform the finalization of the class. Class destructors come with the same advantages as class constructors, except for finalization purposes.

The following example builds on the example shown in class constructors and introduces the finalization routine:

 type

   TBox = class

   private

     class var FList: TList<Integer>;

     class constructor Create;

     class destructor Destroy;

   end;

 

 implementation

 

 class constructor TBox.Create;

 begin

   { Initialize the static FList member }

   FList := TList<Integer>.Create();

 end;

 

 class destructor TBox.Destroy;

 begin

   { Finalize the static FList member }

   FList.Free;

 end;

 

 end

 

Note: The class destructor for a generic class or record may execute multiple times. The exact number of times the class destructor is executed in this case depends on the number of specialized versions of the generic type. For example, the class destructor for a specialized TList<String> class may execute multiple times in the same application.