type TShapeClass = class of TShape; declares a class reference: a variable that holds a class itself, not an instance. It can hold TShape or any descendant, such as TCircle.
Pascal Expert · Lesson 1 · On the house
Class references and virtual constructors
Metaclasses (class of), virtual constructors, and building objects from a registry by name. Lesson 1 is free — on the house.
Lessons · Pascal Expert · Lesson 1 of 10 · On the house
Class references and virtual constructors
Metaclasses (class of), virtual constructors, and building objects from a registry by name.

Calling Cls.Create on a class reference calls the constructor of whatever class it holds, but only if the constructor is virtual in the base and override in descendants. Otherwise the object is still of the held class, but only the base constructor's code runs.
You can also call class methods (class function Kind: string; virtual;) through the reference, with no instance.
That gives you a factory built into the language. Keep classes in a registry (an array, or a dictionary keyed by name), then create instances from configuration strings.
Obj.ClassType returns the object's class reference and Obj.ClassName returns its name. Obj is TCircle tests class membership, including descendants.
virtual constructors
program ClassRefs;
{$mode objfpc}{$H+}
uses SysUtils;
type
TShape = class
Size: Double;
constructor Create(ASize: Double); virtual;
function Area: Double; virtual; abstract;
class function Kind: string; virtual;
end;
TShapeClass = class of TShape;
TSquare = class(TShape)
function Area: Double; override;
class function Kind: string; override;
end;
TCircle = class(TShape)
constructor Create(ASize: Double); override;
function Area: Double; override;
class function Kind: string; override;
end;
constructor TShape.Create(ASize: Double); begin Size := ASize; end;
class function TShape.Kind: string; begin Result := 'shape'; end;
function TSquare.Area: Double; begin Result := Size * Size; end;
class function TSquare.Kind: string; begin Result := 'square'; end;
constructor TCircle.Create(ASize: Double); begin inherited Create(ASize / 2); end;
function TCircle.Area: Double; begin Result := Pi * Size * Size; end;
class function TCircle.Kind: string; begin Result := 'circle'; end;
const Registry: array[0..1] of TShapeClass = (TSquare, TCircle);
function Make(const Name: string; Size: Double): TShape;
var C: TShapeClass;
begin
for C in Registry do
if C.Kind = Name then Exit(C.Create(Size));
raise Exception.Create('unknown ' + Name);
end;
var S: TShape;
begin
S := Make('circle', 2);
writeln(S.ClassName, ' ', S.Size:0:1, ' ', S.Area:0:2, ' ', S is TShape);
S.Free;
S := Make('square', 3);
writeln(S.ClassType.ClassName, ' ', S.Area:0:0);
S.Free;
end.
Quiz
What does 'class of TShape' declare?
Quiz
What must the base constructor be for Cls.Create to call the right descendant constructor?
Quiz
What Size does Make('circle', 2) store?
Quiz
Can a class function be called through a class reference without an instance?
Check
Match the still for: Class references and virtual constructors.
Open-book: the answers are on this page. Pass every quiz and check (5) to mark the lesson done. This visit: 0/5.