Next: , Previous: Type Casts, Up: Programming



6.8 Object-Oriented Programming

GNU Pascal follows the object model of Borland Pascal 7.0. The BP object extensions are almost fully implemented into GPC. This includes inheritance, virtual and non-virtual methods, constructors, destructors, pointer compatibility, extended `New' syntax (with constructor call and/or as a Boolean function), extended `Dispose' syntax (with destructor call).

The Borland object model is different from the ISO draft, but it will not be too difficult now to implement that too (plus the Borland Delphi Object Extensions which are quite similar to the ISO draft).

The syntax for an object type declaration is as follows:

     program ObjectDemo;
     
     type
       Str100 = String (100);
     
       FooParentPtr = ^FooParent;
       FooPtr = ^Foo;
     
       FooParent = object
         constructor Init;
         destructor Done; virtual;
         procedure Bar (c: Real); virtual;
         function Baz (b, a, z: Char) = s: Str100;  { not virtual }
       end;
     
       Foo = object (FooParent)
         x, y: Integer;
         constructor Init (a, b: Integer);
         destructor Done; virtual;
         procedure Bar (c: Real); virtual;  { overrides `FooParent.Bar' }
         z: Real;  { GPC extension: data fields after methods }
         function Baz: Boolean;  { new function }
       end;
     
     constructor FooParent.Init;
     begin
       WriteLn ('FooParent.Init')
     end;
     
     destructor FooParent.Done;
     begin
       WriteLn ('I''m also done.')
     end;
     
     procedure FooParent.Bar (c: Real);
     begin
       WriteLn ('FooParent.Bar (', c, ')')
     end;
     
     function FooParent.Baz (b, a, z: Char) = s: Str100;
     begin
       WriteStr (s, 'FooParent.Baz (', b, ', ', a, ', ', z, ')')
     end;
     
     constructor Foo.Init (a, b: Integer);
     begin
       inherited Init;
       x := a;
       y := b;
       z := 3.4;
       FooParent.Bar (1.7)
     end;
     
     destructor Foo.Done;
     begin
       WriteLn ('I''m done.');
       inherited Done
     end;
     
     procedure Foo.Bar (c: Real);
     begin
       WriteLn ('Foo.Bar (', c, ')')
     end;
     
     function Foo.Baz: Boolean;
     begin
       Baz := True
     end;
     
     var
       Ptr: FooParentPtr;
     
     begin
       Ptr := New (FooPtr, Init (2, 3));
       Ptr^.Bar (3);
       Dispose (Ptr, Done);
       New (Ptr, Init);
       with Ptr^ do
         WriteLn (Baz ('b', 'a', 'z'))
     end.

Remarks:

A pointer to FooParent may be assigned the address of a Foo object. A FooParent formal var parameter may get a Foo object as the actual parameter. In such cases, a call to a virtual method calls the child's method, whereas a call to a non-virtual method selects the parent's one:

     var
       MyFooParent: FooParentPtr;
       SomeFoo: Foo;
     
     [...]
     
       SomeFoo.Init (4, 2);
       MyFooParent := @SomeFoo;
       MyFooParent^.bar (3.14);  { calls `foo.bar' }
       MyFooParent^.baz ('b', 'a', 'z');  { calls `fooParent.baz' }
       if SomeFoo.baz then  { calls `foo.baz' }
         WriteLn ('Baz!');

In a method, an overwritten method of a parent object can be called either prefixing it with the parent type name, or using the keyword inherited:

     procedure Foo.Bar (c: Real);
     begin
       z := c;
       inherited bar (z)  { or: FooParent.Bar (z) }
     end;

Use FooParent.bar (z) if you want to be sure that this method is called, even if somebody decides not to derive foo directly from fooParent but to have some intermediate object. If you want to call the method bar of the immediate parent – whether it be fooParent or whatever – use inherited bar (z).

To allocate an object on the heap, use New in one of the following manners:

     var
       MyFoo: FooPtr;
     
       [...]
     
       New (MyFoo, Init (4, 2));
     
       MyFooParent := New (FooPtr, Init (4, 2))

The second possibility has the advantage that MyFoo needn't be a FooPtr but can also be a FooParentPtr, i.e. a pointer to an ancestor of foo.

Destructors can and should be called within Dispose:

     Dispose (MyFooParent, Fini)