Classes & Objects

A class bundles data (fields) and behavior (methods).

24.1 Declaring a class

A class bundles data (fields) and behavior (methods). Every class descends from TObject. Class variables are references (pointers) to heap-allocated instances.

program BankDemo;
{$mode objfpc}{$H+}
uses SysUtils;

type
  TBankAccount = class
  private
    FOwner: string;
    FBalance: Currency;
  public
    constructor Create(const AOwner: string; AInitial: Currency = 0);
    destructor Destroy; override;
    procedure Deposit(Amount: Currency);
    function Withdraw(Amount: Currency): Boolean;
    function Describe: string;
  end;

constructor TBankAccount.Create(const AOwner: string; AInitial: Currency);
begin
  inherited Create;            // always call the ancestor constructor
  FOwner := AOwner;
  FBalance := AInitial;
end;

destructor TBankAccount.Destroy;
begin
  Writeln('Closing account of ', FOwner);
  inherited Destroy;           // always call the ancestor destructor last
end;

procedure TBankAccount.Deposit(Amount: Currency);
begin
  if Amount <= 0 then
    raise EArgumentException.Create('Deposit must be positive');
  FBalance := FBalance + Amount;
end;

function TBankAccount.Withdraw(Amount: Currency): Boolean;
begin
  Result := (Amount > 0) and (Amount <= FBalance);
  if Result then
    FBalance := FBalance - Amount;
end;

function TBankAccount.Describe: string;
begin
  Result := Format('%s: %.2f', [FOwner, FBalance]);   /
…

24.2 Object lifecycle

var Acc: TBankAccount; just a reference (pointer), initially garbage (local) or nil (global/field) Acc:= TBankAccount.Create(...) allocates zeroed memory, runs the constructor; if the constructor raises, the destructor runs and memory is freed calls Destroy if Acc <> nil; never call Destroy directly Acc:= nil or FreeAndNil(Acc) avoids dangling references

Fields are zero-initialized on creation (numbers 0, strings '', references nil).

Always declare destructors as destructor Destroy; override;.

The standard pattern is create → try → use → finally → Free. Create *before* try; if the constructor fails, there is nothing to free.

Assigning one object variable to another copies the reference, not the object.

var
  A, B: TBankAccount;
begin
  A := TBankAccount.Create('X');
  B := A;               // B refers to the same object
  B.Deposit(10);        // visible through A as well
  A.Free;               // B is now dangling — do NOT free it again
end.

24.3 Self

Inside a method, Self refers to the current instance. It is implicit for member access but useful to pass the object to other routines or to disambiguate (assume procedure TransferTo(Target: TBankAccount; Amount: Currency); was added to the class):

procedure TBankAccount.TransferTo(Target: TBankAccount; Amount: Currency);
begin
  if Target = Self then Exit;
  if Withdraw(Amount) then
    Target.Deposit(Amount);
end;

24.4 Useful TObject members

Create / Destroy / Free construction and destruction class name as a string ('TBankAccount') ClassType / ClassParent metaclass of the instance / of its parent class-level type test bytes per instance Equals, GetHashCode, ToString overridable (Delphi 2009+, FPC 2.4+)

AfterConstruction, BeforeDestruction hooks after the constructor / before the destructor

24.5 Multiple constructors

type
  TPoint3 = class
  public
    X, Y, Z: Double;
    constructor Create; overload;
    constructor Create(AX, AY, AZ: Double); overload;
  end;

constructor TPoint3.Create;
begin
  Create(0, 0, 0);          // calling another constructor on Self: no new allocation
end;

constructor TPoint3.Create(AX, AY, AZ: Double);
begin
  inherited Create;
  X := AX; Y := AY; Z := AZ;
end;

24.6 Old-style objects

Turbo Pascal's object type (still supported by FPC and Delphi) creates value-type objects that can live on the stack, with constructor Init / destructor Done. It predates class; use it only in legacy code or when you need stack-allocated objects with inheritance (advanced records are usually a better choice).