Node:OOP, Next:Compiler Directives, Previous:Type Casts, Up:Programming

### 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): 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:

- Data fields and methods can be mixed.
- GPC currently does not support `private` declarations and such. These directives are syntactically accepted but ignored.
- Constructors and destructors are ordinary functions, internally. When a constructor is called, GPC creates some inline code to initialize the object; destructors do nothing special.
- Currently, the compiler does not check whether all declared methods are really implemented. Unimplemented methods will produce linking errors when they are called or if they are virtual.

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)