Methods are called just as normal procedures are called, only they have an object instance
identifier prepended to them (see also chapter 7, page 248). To determine which method is called,
it is necessary to know the type of the method. We treat the different types in what
follows.
Static methods are methods that have been declared without a abstract or
virtual keyword. When calling a static method, the declared (i.e. compile time) method of the
object is used. For example, consider the following declarations:
Type
TParent = Object
...
procedure Doit;
...
end;
PParent = ^TParent;
TChild = Object(TParent)
...
procedure Doit;
...
end;
PChild = ^TChild;
|
As it is visible, both the parent and child objects have a method called Doit. Consider now the
following declarations and calls:
Var ParentA,ParentB : PParent;
Child : PChild;
ParentA := New(PParent,Init);
ParentB := New(PChild,Init);
Child := New(PChild,Init);
ParentA^.Doit;
ParentB^.Doit;
Child^.Doit;
|
Of the three invocations of Doit, only the last one will call TChild.Doit, the other two calls will
call TParent.Doit. This is because for static methods, the compiler determines at compile time
which method should be called. Since ParentB is of type TParent, the compiler decides that it
must be called with TParent.Doit, even though it will be created as a TChild. There may be
times when the method that is actually called should depend on the actual type of the
object at run-time. If so, the method cannot be a static method, but must be a virtual
method.
To remedy the situation in the previous section, virtual methods are created.
This is simply done by appending the method declaration with the virtual modifier. Going back
to the previous example, consider the following alternative declaration:
Type
TParent = Object
...
procedure Doit;virtual;
...
end;
PParent = ^TParent;
TChild = Object(TParent)
...
procedure Doit;virtual;
...
end;
PChild = ^TChild;
|
As it is visible, both the parent and child objects have a method called Doit. Consider now the
following declarations and calls :
Var ParentA,ParentB : PParent;
Child : PChild;
ParentA := New(PParent,Init);
ParentB := New(PChild,Init);
Child := New(PChild,Init);
ParentA^.Doit;
ParentB^.Doit;
Child^.Doit;
|
Now, different methods will be called, depending on the actual run-time type of the object. For
ParentA, nothing changes, since it is created as a TParent instance. For Child, the
situation also doesn’t change: it is again created as an instance of TChild. For ParentB
however, the situation does change: Even though it was declared as a TParent, it is
created as an instance of TChild. Now, when the program runs, before calling Doit, the
program checks what the actual type of ParentB is, and only then decides which method
must be called. Seeing that ParentB is of type TChild, TChild.Doit will be called.
The code for this run-time checking of the actual type of an object is inserted by the
compiler at compile time. The TChild.Doit is said to override the TParent.Doit. It is
possible to acces the TParent.Doit from within the varTChild.Doit, with the inherited
keyword:
Procedure TChild.Doit;
begin
inherited Doit;
...
end;
|
In the above example, when TChild.Doit is called, the first thing it does is call TParent.Doit.
The inherited keyword cannot be used in static methods, only on virtual methods.
An abstract method is a special kind of virtual method. A method can
not be abstract if it is not virtual (this is not obvious from the syntax diagram). An
instance of an object that has an abstract method cannot be created directly. The reason
is obvious: there is no method where the compiler could jump to ! A method that is
declared abstract does not have an implementation for this method. It is up to inherited
objects to override and implement this method. Continuing our example, take a look at
this:
Type
TParent = Object
...
procedure Doit;virtual;abstract;
...
end;
PParent=^TParent;
TChild = Object(TParent)
...
procedure Doit;virtual;
...
end;
PChild = ^TChild;
|
As it is visible, both the parent and child objects have a method called Doit. Consider now the
following declarations and calls :
Var ParentA,ParentB : PParent;
Child : PChild;
ParentA := New(PParent,Init);
ParentB := New(PChild,Init);
Child := New(PChild,Init);
ParentA^.Doit;
ParentB^.Doit;
Child^.Doit;
|
First of all, Line 3 will generate a compiler error, stating that one cannot generate instances of
objects with abstract methods: The compiler has detected that PParent points to an
object which has an abstract method. Commenting line 3 would allow compilation of the
program.
Remark: If an abstract method is overridden, The parent method cannot be called with inherited, since
there is no parent method; The compiler will detect this, and complain about it, like
this:
testo.pp(32,3) Error: Abstract methods can't be called directly
|
If, through some mechanism, an abstract method is called at run-time, then a run-time error will
occur. (run-time error 211, to be precise)