Programming Using Objects Page 2

From Lazarus wiki
Jump to navigationJump to search

Objects - Self & Inherited

Self Keyword

Objects have an implicit parameter, self, which can be used in method calls which can be used to qualify fields and methods of the object. The explicit use of the keyword is optional but provides clarity if desired. The following implicit and explicit use of the self qualifier for field and method access within a method are identical. To illustrate this, let's say the the following (non useful) code was inserted into the Rectangle.Draw method:

<delphi> procedure Rectangle.Draw; begin

Self.x := 1; x := 1;

Self.Getparams; GetParams;

end; </delphi>

Inherited Keyword

A bit more useful is the keyword inherited for use within an object's methods. By using this keyword, one can explicitly call the parent method of the same name. This is useful because it allows a method overriding it's parent's methods the capability of using the parent's code before or after child object's code is called. The use of the inherited keyword can propagate up and inheritance chain. For our simple example program, there is input code common to all objects; the readln statements. Previously, this code had to be duplicated into the TSquare.GetParams method since it overrode the behavior of it's anscestor object types TRectangle and TShape. Now, using the inherited keyword, the GetParams method for TShape and TSquare can be implemented in the following manner to provide more efficient reuse of code.

<delphi>

procedure TShape.GetParams;

 begin

readln(x, y, width, height); writeln;

 end;
procedure TSquare.GetParams;
 begin

write('TSquare.GetParams : '); inherited; height := width; writeln('making sure all sides are equal for Square');

 end;

</delphi>

Note that there is no Draw method declared for TRectangle and the inherited method from TShape will be called implicitly. When the TSquare.Draw method is executed, it overrides the TShape.Draw method but after calling the writeln statement, it invoking the inherited TShape.Draw method. Then, after the inherited method returns, TSquare.Draw does some post processing. The inherited keyword is often useful in constructors for pre processing task like creating and initializing auxiliary objects and additional fields associated with the object. Similarly, the keyword is also useful in destructors for freeing up auxiliary objects and data structures the object previously created.. It should be noted that allthough code reuse and encapsulation is improved, program flow can be more difficult to follow than for straight procedural code.

Objects - Abstract Methods

Abstract methods are virtual methods which must be overridden in a child object. An abstract method is declared by adding the abstract keyword after the virtual keyword. There are no methods implemented for Objects with abstract methods declared which means no objects with abstract methods can be declared. Child objects must be created to provide overridden methods. The inherited keyword can not be used for child objects to call a parent abstract (non existent) method and depending on the situation, either the compiler will catch this and if it doesn't, a run time error will occur. An example declaration follows.

<delphi> Type

  TNewShape = Object
      f : longint;
     procedure GetParams; virtual;
     procedure Draw; virtual; abstract;
  end;

</delphi>

Although the example object drawing program might want to declare a shape type with abstract methods as shown above, a variable of TShape2 could not be declared which may or may not be a desired program feature.

Objects - Visibility Features

Static Fields

Normally, every instantiated object has its own privately maintained fields (i.e. separate individual memory locations) Child objects inherit the name of the field but the memory location is separate from all the other object fields of the same name qualified by the dot notation or scope. If the static keyword is added after the field name in the type definition, it signifies that this particular field is global for all instantiated object variables throughout the program which also propagates down the inheritance chain. The field then acts like a regular global variable with limited accessibility only via the object dot notation or in the local scope of object's methods. In addition, the type name itself can be used to access the global field. In order to use accidental use of the static field feature, the {$static on} compiler directive must be included in the source file. The following program illustrates the behavior of both regular and static fields.

<delphi> program StaticFieldsForever;

{$static on}

type

Ts1 =object f1 : longint; f2 : longint; static; end;

Ts2 =object (TS1) end;


var A1, B1 : Ts1; A2, B2 : Ts2;

begin writeln;

A1.f1:=2; B1.f1:=3; writeln('f1'); writeln(' A1 :', A1.f1, ' B1 :', B1.f1); writeln(' A2 :', A2.f1, ' B2 :', B2.f1); writeln('-------');

A2.f1:=4; writeln('f1'); writeln(' A1 :', A1.f1, ' B1 :', B1.f1); writeln(' A2 :', A2.f1, ' B2 :', B2.f1); writeln('-------');

B1.f2:=5; writeln('f1'); writeln(' A1 :', A1.f1, ' B1 :', B1.f1); writeln(' A2 :', A2.f1, ' B2 :', B2.f1); writeln('f2'); writeln(' A1 :', A1.f2, ' B1 :', B1.f2, ' T1 :', Ts1.f2); writeln(' A2 :', A2.f2, ' B2 :', B2.f2, ' T2 :', Ts2.f2); writeln('-------');

Ts2.f2:=6; writeln('f2'); writeln(' A1 :', A1.f2, ' B1 :', B2.f2, ' T1 :', Ts1.f2); writeln(' A2 :', A2.f2, ' B2 :', B2.f2, ' T2 :', Ts2.f2); writeln; end. </delphi> Resulting output

f1
 A1 :2  B1 :3
 A2 :0  B2 :0
-------
f1
 A1 :2  B1 :3
 A2 :4  B2 :0
-------
f1
 A1 :2  B1 :3
 A2 :4  B2 :0
f2
 A1 :5  B1 :5 T1 :5
 A2 :5  B2 :5 T2 :5
-------
f2
 A1 :6  B1 :6 T1 :6
 A2 :6  B2 :6 T2 :6

Public, Private, Protected