Two object models
Pascal has two ways to make objects. The object type of Turbo Pascal 5.5
(1989) is a record with methods, kept in the same places as any record, on the stack or the
heap. The class type, which came with Delphi (1995), is always on the heap and
reached through a reference. Free Pascal has both. Classes are the model of Delphi,
Lazarus and almost all modern Pascal; object survives in old code and in
Turbo Vision.
Turbo Pascal objects
type
PShape = ^TShape;
TShape = object
X, Y: Integer;
constructor Init(AX, AY: Integer);
destructor Done; virtual;
procedure Draw; virtual;
end;
TCircle = object(TShape)
R: Integer;
constructor Init(AX, AY, AR: Integer);
procedure Draw; virtual;
end;
var P: PShape;
begin
P := New(PShape, Init(1, 2)); { allocate and construct }
Dispose(P, Done); { destruct and free }
end.
An object may inherit from one other object (object(Parent)). Methods
marked virtual are looked up at run time through the object’s virtual method
table, and an object type with virtual methods must have a constructor that
sets the table up. New(P, Init(…)) and Dispose(P, Done) extend
New and Dispose to call the constructor and destructor. Objects
have private and public sections, but no properties.
Classes
type
TAnimal = class
private
FName: string;
procedure SetName(const Value: string);
protected
procedure Speak; virtual; abstract;
public
constructor Create(const AName: string);
destructor Destroy; override;
property Name: string read FName write SetName;
end;
TDog = class(TAnimal)
protected
procedure Speak; override;
end;
constructor TAnimal.Create(const AName: string);
begin
inherited Create;
FName := AName;
end;
- A class inherits from one parent. With no parent in the declaration it inherits from
TObject, the root of all classes, which suppliesCreate,Free,Destroy,ClassName,ClassType,InheritsFrom,ToStringandEquals. - A variable of a class type holds a reference, which is
nilor points at an object made by calling a constructor:Dog := TDog.Create('Rex');. The program frees the object withFree(a method that callsDestroyonly if the reference is notnil). - A class may also implement any number of interfaces:
TDog = class(TAnimal, IBark, IFeed). - Fields are declared first within a visibility section, then methods and properties. Field and method names inside a method need no prefix;
Selfis the current object. inheritedcalls the parent’s method of the same name (with the same arguments if none are written);inherited Other(…)calls another parent method.- A class method belongs to the class, not an object:
class function Make: TFoo;.class vardeclares a field shared by all objects.class constructorandclass destructorrun once, for the class (Delphi, Free Pascal). - Nested types, constants and
class varfields may be declared inside a class (Delphi 2006 on, Free Pascal).
Visibility
| Section | Who can use the members |
|---|---|
private | The unit that contains the class (any code there), or only the class and its nested types under strict private. |
protected | The class, its descendants and, as for private, any code in the same unit; strict protected leaves out the unit. |
public | Everyone. |
published | Like public, and the members are stored in run-time type information, so the designer, streaming and RTTI can find them. A class needs {$M+} or a TPersistent ancestor. |
automated | Like public, with Automation (OLE) information (Delphi, for Automation servers). |
With no section named, members are public in an object and a class declared
with {$M+} is published by default.
Constructors and destructors
- A constructor is called on a class (
TFoo.Create); it allocates the object, zeroes its fields, runs the body and returns the reference. Called on an object (Obj.Create) it is an ordinary method. - If a constructor raises an exception, the half-built object is destroyed.
- A destructor (
destructor Destroy; override;) releases what the object owns. Callinherited Destroylast. CallFree, neverDestroy, to free an object;FreeAndNil(Obj)also sets the reference tonil. - Constructors can be virtual in classes, so that a class reference
TFooClass = class of TFoocan create an object of an unknown descendant.
Virtual, dynamic, abstract
| Directive | Meaning |
|---|---|
virtual | The method may be replaced in a descendant; the call is resolved at run time by a virtual method table. |
dynamic | The same, resolved by a smaller, slower dynamic table. |
override | Replace an inherited virtual or dynamic method of the same signature. |
reintroduce | Declare a new method of the same name as an inherited virtual one without overriding it (and so without the compiler warning). |
abstract | The method has no body here; descendants must override it. A class with abstract methods cannot sensibly be instantiated. class abstract forbids it. |
final | A virtual method that may not be overridden further. class sealed forbids descendants. |
message | A method called for a Windows message number (message WM_PAINT) or a Free Pascal string message; this is how the VCL and LCL dispatch messages. |
static | A class method that has no Self and can be used as a plain routine (Free Pascal). |
overload | Several methods of the same name with different parameters. |
Properties
property Name: string read FName write SetName; { field read, method write }
property Count: Integer read GetCount; { read only }
property Items[Index: Integer]: TItem read GetItem write SetItem; default; { indexed; X[3] }
property Value: Integer index 2 read GetByIndex write SetByIndex; { one method, many properties }
property Tag: Integer read FTag write FTag stored False default 0;
property Parent: TFoo read FParent implements IParent; { delegates an interface }
A property looks like a field and is read and written through read and
write, each of which names a field or a method. An indexed property
takes parameters in brackets, and one marked default is used by the
shorthand Obj[I]. stored, default and
nodefault control whether a published property is written to a
form file. index passes a constant to the accessor. implements
routes an interface to an object held in a property.
Class references and run-time type information
type TShapeClass = class of TShape;
var C: TShapeClass;
begin
C := TCircle;
S := C.Create; { calls the right constructor }
if S is TCircle then ... { type test }
Circle := S as TCircle; { checked cast }
WriteLn(S.ClassName); { 'TCircle' }
end;
class of T is a variable that holds a class. is tests, and
as casts with a check. ClassType, ClassParent,
InheritsFrom and TypeInfo read the type information.
Interfaces
type
IWriter = interface
['{4E0B1F1C-5C0E-4C3B-9C86-0A2B5D9F7A11}'] { optional GUID }
procedure Write(const S: string);
end;
TFileWriter = class(TInterfacedObject, IWriter)
procedure Write(const S: string);
end;
var W: IWriter;
begin
W := TFileWriter.Create; { reference counted: freed when the last reference goes }
W.Write('hi');
end;
- An interface is a list of methods and properties with no code or fields. A class implements one or more by having methods that match, and may delegate with
implements. - Every interface derives from
IUnknown(also calledIInterface), which hasQueryInterface,_AddRefand_Release, so a variable of an interface type is reference counted: the object is freed when the count reaches zero.TInterfacedObjectimplements this. - An interface may have a GUID in brackets after its heading.
Supports(Obj, IWriter, W)orasconverts an object or interface to another interface through the GUID. - Method resolution clauses rename a clashing method:
procedure IFoo.Run = RunFoo;. dispinterfaceis the Automation (OLE) kind, whose methods are called by name or number throughIDispatch, withdispiddirectives.- Free Pascal also has CORBA-style interfaces (not reference counted,
{$INTERFACES CORBA}) and Objective-C classes and protocols (objcclass,objcprotocol,objccategorywith theobjectivec1andobjectivec2mode switches).
Records with methods, and helpers
type
TPoint = record
X, Y: Integer;
constructor Create(AX, AY: Integer);
function Length: Double;
class operator Add(const A, B: TPoint): TPoint;
end;
TStringHelper = record helper for string
function Twice: string;
end;
TListHelper = class helper for TList
procedure AddTwice(P: Pointer);
end;
- A record may have constructors, methods, properties, constants, class members and operators (Delphi 2006 and later; Free Pascal with
advancedrecords). It has no inheritance and no virtual methods, and lives where a record lives. - A class helper adds methods to an existing class without deriving from it. A record helper does so for a record, and a type helper (
record helper for string) for a simple type such asstringorInteger(Delphi XE3 and later; Free Pascal, with thetypehelpersmode switch). Only one helper for a type is active at a time, the one declared last in scope. - Delphi 10.4 added custom managed records: a record may declare
Initialize,FinalizeandAssignoperators that run on creation, destruction and copy.
Attributes
type
[Weak] TRef = class ... end;
TTests = class
[Test]
[TestCase('Zero', '0,0')]
procedure Check(A, B: Integer);
end;
Delphi 2010 and later accept attributes in square brackets before a type,
field, method, property or parameter. An attribute is a class derived from
TCustomAttribute, named with or without its Attribute ending,
and read back at run time through RTTI (TRttiContext). Free Pascal’s
development version reads them with the prefixedattributes mode switch.
[Weak], [Unsafe] and [Volatile] are
compiler-recognised attributes of Delphi.