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).