Language
日本語
English

Caution

JavaScript is disabled in your browser.
This site uses JavaScript for features such as search.
For the best experience, please enable JavaScript before browsing this site.

  1. Home
  2. Pascal Dictionary
  3. Virtual Methods

Virtual Methods

In Object Pascal, declaring a method with the VIRTUAL keyword and overriding it in a derived class with OVERRIDE enables polymorphism across the entire inheritance tree. Even when a variable is declared as the base class type, the method of the actual class is called at runtime, allowing each class to define its own behavior while sharing a common interface.

Syntax

{ -----------------------------------------------
  Base class — declares virtual methods with VIRTUAL
  ----------------------------------------------- }

type
  TBase = class(TObject)
  public
    { virtual method — can be overridden in derived classes }
    procedure Describe; virtual;
    { regular method — cannot be overridden in derived classes }
    procedure Greet;
  end;

{ -----------------------------------------------
  Derived class — overrides the virtual method with OVERRIDE
  ----------------------------------------------- }

type
  TChild = class(TBase)
  public
    { OVERRIDE replaces the base class virtual method }
    procedure Describe; override;
  end;

{ -----------------------------------------------
  Method implementations
  ----------------------------------------------- }

procedure TBase.Describe;
begin
  WriteLn('TBase.Describe called.');
end;

procedure TBase.Greet;
begin
  WriteLn('Hello (TBase.Greet)');
end;

procedure TChild.Describe;
begin
  inherited Describe;          { calls the base class implementation }
  WriteLn('TChild.Describe called.');
end;

{ -----------------------------------------------
  Polymorphism — using a base class variable to handle derived classes
  ----------------------------------------------- }

var
  obj : TBase;

begin
  obj := TChild.Create;
  try
    obj.Describe;              { TChild.Describe is called at runtime }
  finally
    obj.Free;
  end;
end.

Syntax Reference

Syntax / KeywordDescription
virtualDeclares a method as a virtual method in the base class. Derived classes can override it.
overrideOverrides a virtual method from the base class in a derived class. The signature (parameters and return type) must match the base class.
inheritedCalls the base class method of the same name from within an overriding method.
abstractUsed with virtual; abstract; to declare an abstract method. It has no implementation and must be overridden in all derived classes.
class(TParentClass)Declares a derived class that inherits from the specified class. Omitting it automatically inherits from TObject.
inherited CreateCalls the parent class constructor in the constructor. Must always be called to correctly initialize the inheritance tree.
Dynamic dispatchMethods declared with virtual / override are called based on the actual class type at runtime, not the declared type of the variable.
Static methodA method declared without virtual. The method of the variable's declared type is always called, and declaring a method with the same name in a derived class does not override the base class method.
TObjectThe top-level class that all Object Pascal classes implicitly inherit from. Provides basic methods such as Free and ClassName.

Common Mistakes

Forgetting the override keyword causes method hiding

When redefining in a derived class a method that was declared virtual in the parent class, without override, polymorphism does not work and the parent class method is called instead. The compiler issues a warning — check for it.

type
  TBase = class
    procedure Describe; virtual;
  end;
  TChild = class(TBase)
    procedure Describe;   { override is missing — this is method hiding }
  end;

var
  obj : TBase;
begin
  obj := TChild.Create;
  try
    obj.Describe;   { TBase.Describe is called (not TChild.Describe) }
  finally
    obj.Free;
  end;
end.

Adding override makes dynamic dispatch work correctly.

type
  TChild = class(TBase)
    procedure Describe; override;   { with override, TChild.Describe is called }
  end;

Directly instantiating an abstract class

Trying to Create a class that has abstract methods directly causes a runtime error. Always instantiate through a concrete subclass.

type
  TEva = class
    procedure Activate; virtual; abstract;
  end;

var
  e : TEva;
begin
  e := TEva.Create;    { runtime error: abstract class cannot be instantiated directly }
  e.Activate;
  e.Free;
end.

Create a concrete subclass and instantiate through it.

type
  TEvaUnit01 = class(TEva)
    procedure Activate; override;
  end;

procedure TEvaUnit01.Activate;
begin
  WriteLn('Unit-01 — Awakening mode activated.');
end;

var
  e : TEva;
begin
  e := TEvaUnit01.Create;   { instantiate through the concrete subclass }
  try
    e.Activate;
  finally
    e.Free;
  end;
end.

Sample Code

eva_override.pas
{$mode objfpc}
{ eva_override.pas — demonstrates inheritance and overriding with VIRTUAL / OVERRIDE }
{ Represents Neon Genesis Evangelion pilots and EVA units as a class hierarchy }
{
  Compile and run:
    fpc eva_override.pas && ./eva_override
}

program eva_override;

type
  TEva = class(TObject)
  private
    FUnitNumber : Integer;
    FPilot      : string;
    FSyncRate   : Integer;
  public
    constructor Create(AUnit: Integer; const APilot: string; ASync: Integer);
    destructor  Destroy; override;
    procedure   PrintInfo; virtual;
    procedure   Activate; virtual; abstract;
    procedure   BoardingCheck;
    property    UnitNumber : Integer read FUnitNumber;
    property    Pilot      : string  read FPilot;
    property    SyncRate   : Integer read FSyncRate write FSyncRate;
  end;

type
  TEvaUnit00 = class(TEva)
  public
    constructor Create;
    procedure   PrintInfo; override;
    procedure   Activate; override;
  end;

type
  TEvaUnit01 = class(TEva)
  public
    constructor Create;
    procedure   PrintInfo; override;
    procedure   Activate; override;
  end;

type
  TEvaUnit02 = class(TEva)
  public
    constructor Create;
    procedure   PrintInfo; override;
    procedure   Activate; override;
  end;

constructor TEva.Create(AUnit: Integer; const APilot: string; ASync: Integer);
begin
  inherited Create;
  FUnitNumber := AUnit;
  FPilot      := APilot;
  FSyncRate   := ASync;
  WriteLn('[Online] EVA Unit-', FUnitNumber, ' (Pilot: ', FPilot, ') connection confirmed.');
end;

destructor TEva.Destroy;
begin
  WriteLn('[Offline] EVA Unit-', FUnitNumber, ' connection severed.');
  inherited Destroy;
end;

procedure TEva.PrintInfo;
begin
  WriteLn('  Unit     : EVA Unit-', FUnitNumber);
  WriteLn('  Pilot    : ', FPilot);
  WriteLn('  Sync Rate: ', FSyncRate, ' %');
end;

procedure TEva.BoardingCheck;
begin
  WriteLn('  Boarding : ', FPilot, ' — entry plug boarding complete.');
end;

constructor TEvaUnit00.Create;
begin
  inherited Create(0, 'Rei Ayanami', 60);
end;

procedure TEvaUnit00.PrintInfo;
begin
  inherited PrintInfo;
  WriteLn('  Type     : Prototype');
  WriteLn('  Armament : Positron Rifle — combat record confirmed.');
end;

procedure TEvaUnit00.Activate;
begin
  WriteLn('  [Unit-00] Rei Ayanami — self-sacrifice AT Field deployment initiated.');
end;

constructor TEvaUnit01.Create;
begin
  inherited Create(1, 'Shinji Ikari', 80);
end;

procedure TEvaUnit01.PrintInfo;
begin
  inherited PrintInfo;
  WriteLn('  Type     : General-purpose humanoid decisive weapon (Test Type)');
  WriteLn('  Ability  : Awakening mode — defeats Angels autonomously in uncontrolled state.');
end;

procedure TEvaUnit01.Activate;
begin
  WriteLn('  [Unit-01] Shinji Ikari — awakening mode activated, entering berserk state.');
end;

constructor TEvaUnit02.Create;
begin
  inherited Create(2, 'Asuka Langley Soryu', 98);
end;

procedure TEvaUnit02.PrintInfo;
begin
  inherited PrintInfo;
  WriteLn('  Type     : Mass-production prototype (combat deployment)');
  WriteLn('  Ability  : High-mobility close combat via ultra-high sync rate.');
end;

procedure TEvaUnit02.Activate;
begin
  WriteLn('  [Unit-02] Asuka Langley Soryu — the best pilot fighting at full power!');
end;

var
  evas  : array[0..2] of TEva;
  i     : Integer;

begin
  WriteLn('============================================================');
  WriteLn(' NERV — EVA Activation Sequence Start');
  WriteLn('============================================================');
  WriteLn('');

  evas[0] := TEvaUnit00.Create;
  evas[1] := TEvaUnit01.Create;
  evas[2] := TEvaUnit02.Create;

  try
    WriteLn('');
    WriteLn('============================================================');
    WriteLn(' Unit Status Check (virtual/override dynamic dispatch)');
    WriteLn('============================================================');
    for i := 0 to 2 do begin
      WriteLn('');
      evas[i].PrintInfo;
    end;

    WriteLn('');
    WriteLn('============================================================');
    WriteLn(' Boarding Check (regular method — always calls base class)');
    WriteLn('============================================================');
    WriteLn('');
    for i := 0 to 2 do begin
      evas[i].BoardingCheck;
    end;

    WriteLn('');
    WriteLn('============================================================');
    WriteLn(' Special Ability Activation (abstract virtual -> override)');
    WriteLn('============================================================');
    WriteLn('');
    for i := 0 to 2 do begin
      evas[i].Activate;
    end;

    WriteLn('');
    WriteLn('============================================================');
    WriteLn(' Sync Rate Update');
    WriteLn('============================================================');
    WriteLn('');
    evas[0].SyncRate := 150;
    evas[1].SyncRate := 400;
    WriteLn('  Rei Ayanami   sync rate updated to ', evas[0].SyncRate, ' %.');
    WriteLn('  Shinji Ikari  sync rate updated to ', evas[1].SyncRate, ' %.');

  finally
    WriteLn('');
    WriteLn('============================================================');
    WriteLn(' EVA Shutdown Sequence');
    WriteLn('============================================================');
    WriteLn('');
    for i := 0 to 2 do begin
      evas[i].Free;
    end;
  end;

  WriteLn('');
  WriteLn('============================================================');
  WriteLn(' Sequence Complete');
  WriteLn('============================================================');
end.
fpc eva_override.pas && ./eva_override
Free Pascal Compiler version ...
Linking ./eva_override
============================================================
 NERV — EVA Activation Sequence Start
============================================================

[Online] EVA Unit-0 (Pilot: Rei Ayanami) connection confirmed.
[Online] EVA Unit-1 (Pilot: Shinji Ikari) connection confirmed.
[Online] EVA Unit-2 (Pilot: Asuka Langley Soryu) connection confirmed.

============================================================
 Unit Status Check (virtual/override dynamic dispatch)
============================================================

  Unit     : EVA Unit-0
  Pilot    : Rei Ayanami
  Sync Rate: 60 %
  Type     : Prototype
  Armament : Positron Rifle — combat record confirmed.

  Unit     : EVA Unit-1
  Pilot    : Shinji Ikari
  Sync Rate: 80 %
  Type     : General-purpose humanoid decisive weapon (Test Type)
  Ability  : Awakening mode — defeats Angels autonomously in uncontrolled state.

  Unit     : EVA Unit-2
  Pilot    : Asuka Langley Soryu
  Sync Rate: 98 %
  Type     : Mass-production prototype (combat deployment)
  Ability  : High-mobility close combat via ultra-high sync rate.

============================================================
 Boarding Check (regular method — always calls base class)
============================================================

  Boarding : Rei Ayanami — entry plug boarding complete.
  Boarding : Shinji Ikari — entry plug boarding complete.
  Boarding : Asuka Langley Soryu — entry plug boarding complete.

============================================================
 Special Ability Activation (abstract virtual -> override)
============================================================

  [Unit-00] Rei Ayanami — self-sacrifice AT Field deployment initiated.
  [Unit-01] Shinji Ikari — awakening mode activated, entering berserk state.
  [Unit-02] Asuka Langley Soryu — the best pilot fighting at full power!

============================================================
 Sync Rate Update
============================================================

  Rei Ayanami   sync rate updated to 150 %.
  Shinji Ikari  sync rate updated to 400 %.

============================================================
 EVA Shutdown Sequence
============================================================

[Offline] EVA Unit-0 connection severed.
[Offline] EVA Unit-1 connection severed.
[Offline] EVA Unit-2 connection severed.

============================================================
 Sequence Complete
============================================================
eva_static_dispatch.pas

A demonstration of static dispatch (no virtual). Declaring a method with the same name in a derived class does not override it — the base class method is called when using a base class type variable.

{$mode objfpc}
{
  Compile and run:
    fpc eva_static_dispatch.pas && ./eva_static_dispatch
}
program eva_static_dispatch;

type
  TEva = class(TObject)
  public
    procedure Greet;           { no virtual — static dispatch }
    procedure Activate; virtual;   { virtual — dynamic dispatch }
  end;

type
  TEvaUnit01 = class(TEva)
  public
    procedure Greet;           { method hiding, not overriding }
    procedure Activate; override;
  end;

procedure TEva.Greet;
begin
  WriteLn('TEva.Greet: General-purpose EVA.');
end;

procedure TEva.Activate;
begin
  WriteLn('TEva.Activate: Preparing for activation.');
end;

procedure TEvaUnit01.Greet;
begin
  WriteLn('TEvaUnit01.Greet: Unit-01 — Shinji Ikari is on board.');
end;

procedure TEvaUnit01.Activate;
begin
  WriteLn('TEvaUnit01.Activate: Unit-01 — awakening mode activated.');
end;

var
  obj : TEva;    { declared type is TEva }
begin
  obj := TEvaUnit01.Create;
  try
    obj.Greet;      { static dispatch: TEva.Greet is called }
    obj.Activate;   { dynamic dispatch: TEvaUnit01.Activate is called }
  finally
    obj.Free;
  end;
end.
fpc eva_static_dispatch.pas && ./eva_static_dispatch
Free Pascal Compiler version ...
Linking ./eva_static_dispatch
TEva.Greet: General-purpose EVA.
TEvaUnit01.Activate: Unit-01 — awakening mode activated.
eva_abstract.pas

An example of an abstract class. Instantiation is done through a concrete subclass.

{$mode objfpc}
{
  Compile and run:
    fpc eva_abstract.pas && ./eva_abstract
}
program eva_abstract;

type
  { Abstract base class — Activate must be implemented in derived classes }
  TEva = class(TObject)
  public
    procedure Activate; virtual; abstract;
    procedure ShowUnit; virtual;
  end;

type
  TEvaUnit00 = class(TEva)
  public
    procedure Activate; override;
    procedure ShowUnit; override;
  end;

type
  TEvaUnit01 = class(TEva)
  public
    procedure Activate; override;
    procedure ShowUnit; override;
  end;

procedure TEva.ShowUnit;
begin
  WriteLn('General EVA');
end;

procedure TEvaUnit00.Activate;
begin
  WriteLn('Unit-00 — Rei Ayanami: AT Field deployment initiated.');
end;

procedure TEvaUnit00.ShowUnit;
begin
  WriteLn('Unit-00 (Prototype) — Pilot: Rei Ayanami');
end;

procedure TEvaUnit01.Activate;
begin
  WriteLn('Unit-01 — Shinji Ikari: Awakening mode activated.');
end;

procedure TEvaUnit01.ShowUnit;
begin
  WriteLn('Unit-01 (Test Type) — Pilot: Shinji Ikari');
end;

var
  evas : array[0..1] of TEva;
  i    : Integer;
begin
  evas[0] := TEvaUnit00.Create;
  evas[1] := TEvaUnit01.Create;
  try
    for i := 0 to 1 do begin
      evas[i].ShowUnit;
      evas[i].Activate;
    end;
  finally
    for i := 0 to 1 do
      evas[i].Free;
  end;
end.
fpc eva_abstract.pas && ./eva_abstract
Free Pascal Compiler version ...
Linking ./eva_abstract
Unit-00 (Prototype) — Pilot: Rei Ayanami
Unit-00 — Rei Ayanami: AT Field deployment initiated.
Unit-01 (Test Type) — Pilot: Shinji Ikari
Unit-01 — Shinji Ikari: Awakening mode activated.

Overview

In Object Pascal, declaring a method with virtual in the base class and overriding it with override in the derived class enables dynamic dispatch — the method of the actual object type is called at runtime. Combining virtual; abstract; declares an abstract method with no implementation, forcing all derived classes to provide an override. Methods without virtual use static dispatch, always calling the method of the variable's declared type. Inside an overriding method, inherited can be used to reuse the base class implementation and avoid code duplication. For the basic class declaration and how to use constructors and destructors, see CLASS (class basics). For managing multiple files with units, see UNIT (unit basics).

If you find any errors or copyright issues, please .