Programming Using Objects Page 2: what changed
Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.
Title: Programming Using Objects Page 2
| Old | New | Line | |
|---|---|---|---|
| 1 | + | Previous article: [[Programming Using Objects]] | |
| 2 | + | ||
| 3 | + | ## Objects - Self & Inherited | |
| 4 | + | ||
| 5 | + | ### Self Keyword | |
| 6 | + | ||
| 7 | + | 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: | |
| 8 | + | ||
| 9 | + | ``` pascal | |
| 10 | + | procedure Rectangle.Draw; | |
| 11 | + | begin | |
| 12 | + | ||
| 13 | + | Self.x := 1; | |
| 14 | + | x := 1; | |
| 15 | + | ||
| 16 | + | Self.Getparams; | |
| 17 | + | GetParams; | |
| 18 | + | end; | |
| 19 | + | ``` | |
| 20 | + | ||
| 21 | + | ### Inherited Keyword | |
| 22 | + | ||
| 23 | + | A bit more useful is the **inherited** keyword for use within an object's methods. By using this keyword, one can explicitly call the parent method of the same or different method 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's code is called. The use of the **inherited** keyword can propagate up and inheritance chain. For our simple example program, the readln input code in the GetParams method is a common task for all objects. Previously, this code had to be duplicated into the *TSquare.GetParams* method since it added to the simple input task but in doing so overrode the entire behavior of the ancestor methods. By using the **inherited** keyword, the *GetParams* method for *TSquare* can reuse the basic readln input without having to duplicate it. Consider the previous program implementing these GetParams methods. | |
| 24 | + | ||
| 25 | + | ``` pascal | |
| 26 | + | procedure TShape.GetParams; | |
| 27 | + | begin | |
| 28 | + | readln(x, y, width, height); | |
| 29 | + | writeln; | |
| 30 | + | end; | |
| 31 | + | ||
| 32 | + | procedure TSquare.GetParams; | |
| 33 | + | begin | |
| 34 | + | write('TSquare^.GetParams : '); | |
| 35 | + | inherited; | |
| 36 | + | height := width; | |
| 37 | + | writeln('made sure all sides are equal'); | |
| 38 | + | writeln; | |
| 39 | + | write('Inherited Draw : '); | |
| 40 | + | inherited Draw; | |
| 41 | + | end; | |
| 42 | + | ||
| 43 | + | Var | |
| 44 | + | Square : PSquare; | |
| 45 | + | ||
| 46 | + | begin | |
| 47 | + | Square^.GetParams; | |
| 48 | + | end. | |
| 49 | + | ``` | |
| 50 | + | ||
| 51 | + | TSquare.GetParams : 1 2 3 4 | |
| 52 | + | ||
| 53 | + | made sure all sides are equal | |
| 54 | + | ||
| 55 | + | Inherited Draw : TRectangle.Draw | |
| 56 | + | ||
| 57 | + | Note that *TRectangle* does not declare a *Draw* method and implicitly inherits the draw method from *TShape*. The *TSquare.GetParams* method overrides the *TShape. GetParams* method but after calling the writeln statement, it invokes the *TShape.GetParams* method using the **inherited** keyword. Then, after the inherited method returns, *TSquare.GetParams* does some additional processing afterwards. The **inherited** keyword can be used in another manner which is illustrated by the TSquare.Getparameter method calling the ancestor Draw method. Explicitly calling inherited *Draw* method method in this case may not make much logical sense but is shown to provide an example of the syntax. Even in situations it does make logical sense, use of the inherited keyword in this manner can result in hard to follow program flow. | |
| 58 | + | ||
| 59 | + | 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. | |
| 60 | + | ||
| 61 | + | ## Objects - Abstract Methods | |
| 62 | + | ||
| 63 | + | **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. | |
| 64 | + | ||
| 65 | + | ``` pascal | |
| 66 | + | Type | |
| 67 | + | TShape2 = Object | |
| 68 | + | f : longint; | |
| 69 | + | procedure GetParams; virtual; | |
| 70 | + | procedure Draw; virtual; abstract; | |
| 71 | + | end; | |
| 72 | + | ``` | |
| 73 | + | ||
| 74 | + | 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. | |
| 75 | + | ||
| 76 | + | ## Objects - Visibility Features | |
| 77 | + | ||
| 78 | + | ### Static Fields | |
| 79 | + | ||
| 80 | + | 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. | |
| 81 | + | ||
| 82 | + | ``` pascal | |
| 83 | + | program StaticFieldsForever; | |
| 84 | + | ||
| 85 | + | {$static on} | |
| 86 | + | ||
| 87 | + | type | |
| 88 | + | Ts1 = object | |
| 89 | + | f1 : longint; | |
| 90 | + | f2 : longint; static; | |
| 91 | + | end; | |
| 92 | + | ||
| 93 | + | Ts2 = object (TS1) | |
| 94 | + | end; | |
| 95 | + | ||
| 96 | + | var | |
| 97 | + | A1, B1 : Ts1; | |
| 98 | + | A2, B2 : Ts2; | |
| 99 | + | ||
| 100 | + | begin | |
| 101 | + | writeln; | |
| 102 | + | ||
| 103 | + | A1.f1:=2; | |
| 104 | + | B1.f1:=3; | |
| 105 | + | writeln('f1'); | |
| 106 | + | writeln(' A1 :', A1.f1, ' B1 :', B1.f1); | |
| 107 | + | writeln(' A2 :', A2.f1, ' B2 :', B2.f1); | |
| 108 | + | writeln('-------'); | |
| 109 | + | ||
| 110 | + | A2.f1:=4; | |
| 111 | + | writeln('f1'); | |
| 112 | + | writeln(' A1 :', A1.f1, ' B1 :', B1.f1); | |
| 113 | + | writeln(' A2 :', A2.f1, ' B2 :', B2.f1); | |
| 114 | + | writeln('-------'); | |
| 115 | + | ||
| 116 | + | B1.f2:=5; | |
| 117 | + | writeln('f1'); | |
| 118 | + | writeln(' A1 :', A1.f1, ' B1 :', B1.f1); | |
| 119 | + | writeln(' A2 :', A2.f1, ' B2 :', B2.f1); | |
| 120 | + | writeln('f2'); | |
| 121 | + | writeln(' A1 :', A1.f2, ' B1 :', B1.f2, ' T1 :', Ts1.f2); | |
| 122 | + | writeln(' A2 :', A2.f2, ' B2 :', B2.f2, ' T2 :', Ts2.f2); | |
| 123 | + | writeln('-------'); | |
| 124 | + | ||
| 125 | + | Ts2.f2:=6; | |
| 126 | + | writeln('f2'); | |
| 127 | + | writeln(' A1 :', A1.f2, ' B1 :', B2.f2, ' T1 :', Ts1.f2); | |
| 128 | + | writeln(' A2 :', A2.f2, ' B2 :', B2.f2, ' T2 :', Ts2.f2); | |
| 129 | + | writeln; | |
| 130 | + | end. | |
| 131 | + | ``` | |
| 132 | + | ||
| 133 | + | Resulting output | |
| 134 | + | ||
| 135 | + | f1 | |
| 136 | + | A1 :2 B1 :3 | |
| 137 | + | A2 :0 B2 :0 | |
| 138 | + | ------- | |
| 139 | + | f1 | |
| 140 | + | A1 :2 B1 :3 | |
| 141 | + | A2 :4 B2 :0 | |
| 142 | + | ------- | |
| 143 | + | f1 | |
| 144 | + | A1 :2 B1 :3 | |
| 145 | + | A2 :4 B2 :0 | |
| 146 | + | f2 | |
| 147 | + | A1 :5 B1 :5 T1 :5 | |
| 148 | + | A2 :5 B2 :5 T2 :5 | |
| 149 | + | ------- | |
| 150 | + | f2 | |
| 151 | + | A1 :6 B1 :6 T1 :6 | |
| 152 | + | A2 :6 B2 :6 T2 :6 | |
| 153 | + | ||
| 154 | + | ### Public, Private, Protected | |
| 155 | + | ||
| 156 | + | These specifiers are used for blocks of fields and methods declared in object types. The definitions of these specifiers are detailed in the language reference guide. Basically, **public** (or non specified) fields or methods are visible anywhere in the global scope of the program. **Protected** fields and methods are visible everywhere within the scope of a unit but limited only to objects and descendant objects outside of the unit they are declared in. Private fields and methods are only visible to objects and descended objects of the declared type. An example declaration follows: | |
| 157 | + | ||
| 158 | + | ``` pascal | |
| 159 | + | type | |
| 160 | + | TMyObject =object | |
| 161 | + | f0: longint; | |
| 162 | + | procedure m0; | |
| 163 | + | public | |
| 164 | + | f1 : longint; | |
| 165 | + | procedure m1; | |
| 166 | + | protected | |
| 167 | + | f2 : longint; | |
| 168 | + | procedure m2; | |
| 169 | + | private | |
| 170 | + | f3 : longint; | |
| 171 | + | procedure m3; | |
| 172 | + | public | |
| 173 | + | f4 : longint; | |
| 174 | + | procedure m4; | |
| 175 | + | end; | |
| 176 | + | ``` | |
| 177 | + | ||
| 178 | + | The fields f, f1 and f4 and methods m0, m1 and m4 all have public visibility; f1 and m1 by default (no specifier.) Also, the order or repetition of visibility blocks is fairy flexible as can be seen from the above example. | |
| 179 | + | ||
| 180 | + | ## Objects - Run Time Library | |
| 181 | + | ||
| 182 | + | FPC provides several predefined Object types for this dialect. By convention, all the supplied object types and sub types start with the letter "T". These object types are included in the **OBJECTS** unit. A similar looking set of types is suplied for the Classes object dialect which are declared in the **SYSTEM** unit.. This can get confusing especially as both run time units declare a base object type (or class): *TObject*. | |
| 183 | + | ||
| 184 | + | For this Object dialect, *TObject* is the implicit root object which all objects descend from. When no sub object is specified in an object type declaration, the type *TObject* is implicitly declared for you. | |
| 185 | + | ||
| 186 | + | These predefined object types are documented in the FPC Run-Time Library Reference Guide, chapter 21. | |
| 187 | + | ||
| 188 | + | <ftp://ftp.freepascal.org/pub/fpc/docs-pdf/rtl.pdf> | |
| 189 | + | ||
| 190 | + | This unit documents basic types and support routines as well as the following object types: | |
| 191 | + | ||
| 192 | + | --- | |
| 193 | + | *From the Free Pascal wiki, page "Programming Using Objects Page 2", as it stood on 2020-02-23 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).* | |
| 194 | + | Category: FPC | |
| 195 | + | Category: Tutorials |
