Object Pascal is the programming language you use in Delphi. It is mostly similar to Turbo Pascal, but Borland has added some features to it. I will deal with these later. Object Pascal is obviously an object oriented language. For those who don't know what this is I'll give a brief summary in the next section. Those who are familiar with OOP can skip it.
The idea of OOP is to put both the data and the program in a single container. This container is called object. What you would noramlly declare like this:
var MyByte: Byte; Name: String; procedure DoSomething; function Whatever: Byte;
Can be summed up to a single object. You specify an oject by using the "object"-directive:
type PMyObject = ^TObject; TMyObject = object MyByte: Byte; Name: String; procedure DoSomething; function Whatever: byte; end;
Note that this does not declare the object you will later use. It merily provides a type (think of it as a "template"). You can use this template to create objects from it. To do this use:
var MyObject: PMyObject; begin MyObject:= TMyObject.Create; //... MyObject.Free; end.
"MyObject" is what you can work with. You also need to tell Delphi to create the object (create is called constructor). This will reserve memory form it. When you are done using it, you should free this memory using MyObject.Free; (free is destructor).
What makes objects powerful is that they can inherit variables and methods (constructors, destructors, functions and procedures) from other objects. If you need an object that is just the same as MyObject but has an additional FirstName: String variable, you can use inheritance to achieve this:
type TMySecondObject = object(TMyObject) FirstName: String; end;
There is no need to reprogram all stuff that you already specified in TMyObject.
The object concept is taken from the Turbo Pascal days. The components in Delphi are all classes. Classes are very similar to objects. The main difference is in the declaration. Classes are always Pointers, you do not need to declare this any more. PMyObject as a class would look like this:
type TMyClass = class MyByte: Byte; Name: String; procedure DoSomething; function Whatever: byte; end;
Inheritance works the same way as with objects. There are different sections in a class: private, protected, public, and published. Example:
type TMyClass = class private Password: String; procedure ModifyPassword; protected Username: String; public Phonenumber: String; end;
The password should not be visible from outside the object (this means only methods of the object can access it). However, a private member is not as private as you might think. Inside the unit in which the class is declared, it is accessible as well. (This is similar to the friend directive in C++.)
The protected directive somewhat relaxes the restrictions of private. Protected members are visible to all decendantes of the class.
Public members are literally public - they are accessible from anywhere (if the class is visible).
Published members behave the same as public ones. The difference is that for them runtime type information is generated. This means that outside applications can get information about the members (for details please refer to Delphi's online help system since this is not exactly something you need when getting started with Delphi).
An object can inherit methods from its ancestors - very nice. But what do you do if you want to change the behavior of an object? Lets say you are writing a program with some graphical stuff and you have these objects:
TThing = Object // ... procedure draw; end; TBox = Object(TThing) // ... procedure draw; end; TCircle = Object(TThing) // ... procedure draw; end;
TBox and TCircle are successors of TThing because this eases up drawing your graphics. You can just call AThing.Draw without knowing if AThing is really a TBox or a TCircle. However, you would never call the draw method of TThing - what should TThing draw?
Since TThing.Draw is not supposed to ever be called, you should declare it as an abstract method. When an abstract method is called the program exits with an exception. This makes it easy to find senseless method calls. The functionality of the Draw method is only implemented in the successor objects. They know what they look like and hence can draw something on the screen.
How does Delphi know which Draw method to call if you write AThing.Draw (and AThing is a TBox)? The answer is that you have to declare methods that you want to overwrite as virtual. Simply use the keyword virtual behind the method's declaration. To tell Delphi to overwrite a method in a successor object use the keyword override. Our objects now look like this:
TThing = Object // ... procedure draw; virtual; abstract; end; TBox = Object(TThing) // ... procedure draw; override; end; TCircle = Object(TThing) // ... procedure draw; override; end;
How does Delphi find the correct method at runtime (when typecasting an object)? The answer lies in the VMT (Virtual Method Table). As you can guess from the name this is a table with all virtual methods of the object. There is a copy of the VMT created each time you inherit an object (this means that the single instances do not have there own copies of the VMT - would hardly make sense). When you call a virtual method it will be looked up in the VMT and the correct implementation will automatically be run (in case you overrode a virtual method from an ancestor object).
Dynamic methods are similar to the virtual ones. The difference is that the DMT (Dynamic Method Table) is completely not copied every time you instanciate the object. There are links back to precessor objects. When a dynamic method is called it is first looked up in the DMT of the object itself. If it is not found the DMT of the ancestors are searched. The saves the extra memory you need for copying the whole VMT every time you inherit. Using dynamic methods only makes sense in some special cases (objects with a lot of dynamic/vitual methods (>100) and about as many inheritance steps). In general you should stick to virtual ones.
Second Day • Fourth Day
Please e-mail me with any comments!
© 27.12.96 Sebastian Boßung
• Home