Delphi Classes and Objects

Classes in Delphi
A Class consists of fields, methods and properties which defines character and behavior of an object. Instances of a Class are called as Objects and fields, methods are called as class members.
-> A Field is essentially a variable that  is part of an object.
-> Methods are procedures or functions that associated with a class and which operates on Class Objects.
-> Property is an interface to data associated with an Object. A property have access specifiers which defines how the data will be read and modified.

Objects are dynamically allocated blocks of memory whose structure is determined by their Class type. Each object has a unique copy of every field defined in the class, but all instances of a class share the same methods. Objects are created and destroyed by special methods called Constructor and Destructor.

A variable of a Class is actually a pointer that references an object. Hence more than one variable can refer to the same object. In Delphi a Class type variable can hold the value nil if not initiated. 

A Class  must be declared and given a name before it can be instantiated. A class type declaration has the following form:
type
   className = class [abstract | sealed] (ancestorClass)
      memberList
   end;
->  Classname is a valid class name
-> Class is keyword used to declare a class in Delphi by inheriting from a ancestor class.
->  Abstract / Sealed  keywords are optional. Abstract keyword is used to make an entire class as an abstract class. Sealed keyword is used to make a class as final class from which we cannot inherit a new class. A class cannot be both Abstract and Sealed.
-> AncestorClass is the base class from which we want to inherit that new class. If we don't provide any base class then by default it will be TObject.
type TMyClass = class
      ...
     end;
is equivalent to:
type TMyClass = class(TObject)
      ...
     end;
TObject is the base class of all Delphi classes. And it is declared in System unit.

Access of Class Members
Accessibility of every class members can be defined by access specifiers i.e private, protected, public, published, or automated.
Private   -> A private member is not accessible outside of the unit or program where its class is declared. But if we have declared multiple classes in a single unit then we can access Private members of one class in another class. So if we don't want to do this then we need to declare that member as Strict Private.
Protected -> A protected member is accessible anywhere in the module where its class is declared and from any descendent class, regardless of the module where the descendent class appears. We can use Strict Protected keyword to allow accessible only on its descendent classes.
Public    -> A public member is accessible wherever its class can be referenced.
Published -> A Published member have same visibility as like Public members. The only difference is that Delphi generates RTTI information for Published members. RTTI is used to access the values of properties when saving and loading form files. And we can see published properties of an object in the Object Inspector.
Automated -> Automated members have also same visibility like Public members but the difference is that Automation type information (required for Automation servers) is generated for automated members. Automated members typically appear only in Win32 classes. And it is used for Delphi backward compatibility.

Example
// This is an actual class definition //
  TFruit = Class(TObject)  
    // Internal class field definitions - only accessible in this unit //
    private
      isRound  : Boolean;
      length   : single;
      width    : single;
      diameter : single;
    // Fields and methods only accessible by this class and descendants //
    protected
    // Externally accessible fields and methods //
    public
      // 2 constructors - one for round fruit, the other long fruit //
      constructor Create(diameter : single);               overload;
      constructor Create(length : single; width : single); overload;
    // Externally accessible and inspectable fields and methods //
    published
      // Note that properties must use different names to local defs //
      property round : Boolean read   isRound   write   isRound;
      property len   : single  read   length    write   length;
      property wide  : single  read   width     write   width;
      property diam  : single  read   diameter  write   diameter;
  end;                    
// End of the TFruit class definition //

// Class members implementation //

// Create a round fruit object
constructor TFruit.Create(diameter: single);
begin
  // Indicate that we have a round fruit, and set its size
  isRound       := true;
  self.diameter := diameter;
end;

// Create a long fruit object
constructor TFruit.Create(length, width: single);
begin
  // Indicate that we have a long fruit, and set its size
  isRound     := false;
  self.length := length;
  self.width  := width;
end;

// Using above class with creating Objects of that class //
// We are creating objects on a Form create //
procedure TForm1.FormCreate(Sender: TObject);
var
  apple, banana : TFruit;
begin
  // Let us create our fruit objects
  apple  := TFruit.Create(3.5);
  banana := TFruit.Create(7.0, 1.75);

  // Show details about our fruits
  ShowFruit(apple);
  ShowFruit(banana);
end;

Class Inheritance in Delphi
In Delphi when we declare a Class we can provide an ancestor class immediately from which that new class will inherit all the Protected, Public and Published members automatically. That new class can have its own members also and can redefine the inherited ones but cannot removed base class members.
Example
type
TApple = Class(TFruit)
private
fTest: string;
public
property Test: string read fTest write fTest;
end;
On above example we have created a new class TApple by inheriting from ancestor class TFruit. So now TApple will inherit all members for TFruit and have also own new members like Test. So an object of TApple class can access round, len, diam, wide of TFruit class.

Inherited
This keyword is mostly used in implementing polymorphic behavior to call a base class method. If we don't pass any base class method name then it will call base class method having same name where it is called.
inherited Create(...);
or
inherited; 

Compatibility of Class Types
A class type assignment is compatible with its ancestors. Hence a variable of a base class type can reference an instance of any descendant type. For example, given the declarations:
type
  TFigure = class(TObject);
  TRectangle = class(TFigure);
  TSquare = class(TRectangle);
var
  Fig: TFigure;
Above variable Fig can be assigned values of type TFigure, TRectangle, and TSquare.

Forward Declarations and Mutually Dependent Classes
Sometime we need to declare a Class member having type of another class or if two classes are mutually dependent by using each other type then in this case we have to define class forward declaration. So that it will not show any compile error.
We can do forward declarations fo classes in following way ...
type
  TFigure = class;   // forward declaration //
  TDrawing = class
    Figure: TFigure; // here we have used Figure of TFigure Type. But TFigure is declare after this class. If we wont do forward declaration then it will show compile error//
    // ...//
  end;

  TFigure = class   // defining declaration //
    Drawing: TDrawing; // here we are using TDrawing type which is already declared before this class declaration. So no need to do forward declaration //
    // ...//
  end;

Self
Self references to the current object of the class where it is used.
 function TForm1.TForm1.Button1Click(Sender: TObject);
 var
Text1: TEdit;
 begin
     Text1 := TEdit.Create(Self); // here we are pass Current TForm object as owner to Text1 create //
 end;

 Self.height := 400; // here we are setting height of Current TForm Object //

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. A class can have more than one constructor, but most have only one. Constructors can be overload and override.
Examples:
 constructor Create;
 constructor Create(AOwner: TComponent);
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. 

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;
To call a destructor, you must reference an instance object. But in Delphi we generally call Free/FreeandNil method to free and object. Free/FreeandNil method internally calls Destroy method. So it always better to override Destroy method if required.
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. And during Constructor call if any exception occurred then the Destroy destructor is automatically called to destroy the unfinished object. 

Class Operators
Class Operators are used during runtime type checking and type casting. In Delphi we use IS and AS class operators.
IS -> IS operator is used for runtime type checking which verifies that the Object belongs to a specific class or not.
Example
var
Fruit1: TFruit;
......
 if Fruit1 is TApple then // here we are checking that if Fruit1 object is of type TApple then process //
DoSomething;
AS -> AS operator is used for runtime type casting. 
Example...
 if Fruit1 is TApple then // here we are checking that if Fruit1 object is of type TApple then process //
(Fruit1 As TApple).Test;
Class Helpers
Class Helper is a type associated with another Class used to introduce additional methods and properties. Helpers are a way to extend a class without using Inheritance.
The syntax for declaring a class helper is:
type
   identifierName = class|record helper [(ancestor list)] for TypeIdentifierName
     memberList
   end;
Example...
type
    TMyClass = class
       procedure MyProc;
       function  MyFunc: Integer;
    end;
    ...
    procedure TMyClass.MyProc;
    var X: Integer;
    begin
       X := MyFunc;
    end;
    function TMyClass.MyFunc: Integer;
    begin
        ...
    end;
 ...
 type
    TMyClassHelper = class helper for TMyClass
      procedure HelloWorld;
      function MyFunc: Integer;
    end;
    ...
    procedure TMyClassHelper.HelloWorld;
    begin
       Writeln(Self.ClassName); // Self refers to TMyClass type, not TMyClassHelper
    end;
    function TMyClassHelper.MyFunc: Integer;
    begin
      ...
    end;
 ...
 var
   X: TMyClass;
 begin
   X := TMyClass.Create;
   X.MyProc;    // Calls TMyClass.MyProc
   X.HelloWorld; // Calls TMyClassHelper.HelloWorld
   X.MyFunc;    // Calls TMyClassHelper.MyFunc

Comments

Popular posts from this blog

ShellExecute in Delphi

How to send Email in Delphi?

Variants in Delphi. Use of Variant Array. How to check a Variant is unassigned or empty or clear ?