Objektorientierte Programmierung in Delphi

22
O O b b j j e e k k t t o o r r i i e e n n t t i i e e r r t t e e P P r r o o g g r r a a m m m m i i e e r r u u n n g g i i n n D D e e l l p p h h i i Spezialgebiet Informatik Manuel Pöter – 07.01.2003

Transcript of Objektorientierte Programmierung in Delphi

Page 1: Objektorientierte Programmierung in Delphi

OObbjjeekkttoorriieennttiieerrttee PPrrooggrraammmmiieerruunnggiinn DDeellpphhii

Spezialgebiet Informatik

Manuel Pöter – 07.01.2003

Page 2: Objektorientierte Programmierung in Delphi

Spezialgebiet – Informatik Manuel PöterOOP in Delphi GRG XV Auf der Schmelz

IInnhhaallttssvveerrzzeeiicchhnniiss

Allgemeine Einführung __________________________________________ 1

1 Eigenschaften der objektorientierten Programmierung __________________1

2 Klassen___________________________________________________________12.1 Was sind Klassen? __________________________________________________________ 12.2 Eigenschaften von Klassen ____________________________________________________ 1

2.2.1 Kapselung_____________________________________________________________ 1

2.2.2 Vererbung_____________________________________________________________ 2

2.2.3 Polymorphie ___________________________________________________________ 2

3 Objekte __________________________________________________________23.1 Was ist ein Objekt?__________________________________________________________ 23.2 Attribute und Methoden ______________________________________________________ 3

Objekte in Delphi _______________________________________________ 4

1 Die Klassendeklaration _____________________________________________4

2 Aller Anfang ist TObject ____________________________________________4

3 Kapselung ________________________________________________________53.1 private ____________________________________________________________________ 53.2 public ____________________________________________________________________ 63.3 protected __________________________________________________________________ 63.4 published__________________________________________________________________ 6

4 Methoden_________________________________________________________64.1 Statische __________________________________________________________________ 74.2 Virtuelle __________________________________________________________________ 74.3 Dynamische _______________________________________________________________ 74.4 Botschaftsverarbeitende Methoden______________________________________________ 74.5 Hintergrund – Die Arbeitsweise von Windows ____________________________________ 74.6 Implementierung – Schlüsselwort message _______________________________________ 8

5 Konstruktor und Destruktor_________________________________________85.1 Objektvariablen und Instanzen _________________________________________________ 85.2 Der Konstruktor ____________________________________________________________ 95.3 Der Destruktor ____________________________________________________________ 10

6 Überschreiben von Methoden _______________________________________106.1 Aufruf geerbter Methoden – inherited __________________________________________ 11

7 Überladen von Methoden __________________________________________12

8 Felder und Eigenschaften __________________________________________128.1 Zugriffsmethoden __________________________________________________________ 12

9 Ereignisse _______________________________________________________13

Page 3: Objektorientierte Programmierung in Delphi

Spezialgebiet – Informatik Manuel PöterOOP in Delphi GRG XV Auf der Schmelz

9.1 Prozedurale Typen _________________________________________________________ 149.2 Methodenzeiger ___________________________________________________________ 149.3 Auslösen von Ereignissen____________________________________________________ 15

10 Klassenmethoden _________________________________________________16

11 Forward-Deklarationen ____________________________________________16

12 Abstrakte Klassen ________________________________________________17

13 Weitere wichtige Schlüsselwörter____________________________________1913.1 Operator – is ______________________________________________________________ 1913.2 Operator – as______________________________________________________________ 1913.3 Parameter – Self ___________________________________________________________ 19

Page 4: Objektorientierte Programmierung in Delphi

Spezialgebiet – Informatik Manuel PöterOOP in Delphi GRG XV Auf der Schmelz

1

AALLLLGGEEMMEEIINNEE EEIINNFFÜÜHHRRUUNNGG

Objektorientierte Programmierung (kurz OOP, Object Oriented Programming) ist eines dergrundlegenden Konzepte moderner Softwareentwicklung. Die folgenden Seiten beziehen sichzwar speziell auf Delphi, treffen im Allgemeinen aber auch auf alle anderen objektorientiertenSprachen zu. OOP ist ein sehr komplexes und kompliziertes Thema. Anfangs ist es sehrschwer diese Thematik zu verstehen, aber mit hinreichend praktischen Erfahrungen fällt eseinem wie Schuppen von den Augen und man lernt objektorientiert zu denken.

Im Übrigen gehe ich davon aus, dass man mit der Object-Pascal Syntax vertraut ist und aucheinige Grundkenntnisse besitzt. In einigen Kapiteln ist auch ein gutes Verständnis vonZeigern nötig.

11 EEiiggeennsscchhaafftteenn ddeerr oobbjjeekkttoorriieennttiieerrtteenn PPrrooggrraammmmiieerruunnggDie objektorientierte Programmierung sieht die Bestandteile eines Programms als eineZusammenstellung voneinander weitgehend unabhängiger Teilprogramme an. DieseTeilprogramme werden Klassen genannt. Eine Klasse ist die in einer Programmiersprachegeschriebene Konstruktionsvorschrift für ein Objekt. Das eigentliche Objekt entsteht in demAugenblick, in dem der übersetzte Quellcode einer Klasse im Speicher aktiviert wird. Objektestellen auf Anforderung dem System bestimmte Eigenschaften zur Verfügung. Klassen undObjekte sind eng verbundene Begriffe, die oft miteinander verwechselt werden, aber dennochbesteht ein großer Unterschied zwischen ihnen – das Verhältnis Objekt zu Klasse ist dasselbewie das Verhältnis Variable zu Variablentyp.

22 KKllaasssseenn

22..11 WWaass ssiinndd KKllaasssseenn??Eine Klasse ist sozusagen die Vorlage für das Objekt. Sie enthält die Konstruktionsvorschriftwie das Objekt aufgebaut ist und ist somit die Voraussetzung für die erfolgreicheInitialisierung eines Objektes. Oft hört man auch den Begriff „Objekttyp“ – dieser istgleichbedeutend mit dem Begriff „Klasse“.

22..22 EEiiggeennsscchhaafftteenn vvoonn KKllaasssseennKlassen haben drei wichtige Eigenschaften. Dies sind die Basisprinzipien durch die OOP erstso mächtig wird:

22..22..11 KKaappsseelluunnggEine Klasse bildet eine in sich geschlossene Einheit, bei der nur bestimmte extrafreizugebende Funktionen von außen zugänglich sind. Alle anderen Funktionen der Klassesind private Funktionen und können nur innerhalb der Klasse aufgerufen werden. Gleichesgilt für die Daten, die eine Klasse bearbeitet und für die innerhalb der Klasse verwendetenVariablen. Diese sind, soweit nicht anders vereinbart, ebenfalls privat und somit vor Zugriffenvon außen geschützt. Der Vorteil dieser Eigenschaft liegt auf der Hand: Von anderenProgrammteilen kann weder auf Funktionen noch auf Daten einer Klasse unkontrolliertzugegriffen werden. Ist die Klasse selbst sauber programmiert und ausgetestet sindSeiteneffekte nahezu ausgeschlossen.

Page 5: Objektorientierte Programmierung in Delphi

Spezialgebiet – Informatik Manuel PöterOOP in Delphi GRG XV Auf der Schmelz

2

22..22..22 VVeerreerrbbuunnggEs ist nicht sinnvoll, für jede erdenkliche Eigenschaft eines Programmteiles eine spezielleKlasse zu erstellen. Die Menge der dafür erforderlichen Klassen müsste gigantisch sein.Deswegen bietet OOP die Möglichkeit, dass eine Klasse seine Eigenschaften an eine neueKlasse mit zusätzlichen Eigenschaften weitergibt. Die neue beerbte Klasse wird abgeleiteteKlasse (engl. derived class) genannt, die vererbende Klasse ist die Basisklasse (engl. baseclass). Die abgeleitete Klasse hat Zugriff auf zugelassene Bereiche der Basisklasse. Vonaußen sind auch in diesem Fall nur die extra freigegebenen Funktionen zugänglich.

Ein gedachtes Beispiel veranschaulicht den Vorgang der Vererbung. Eine Basisklasse soll dieFähigkeit haben, auf einer GUI ein Fenster darzustellen. Dieses Fenster hat bestimmteEigenschaften. Es hat wahlfreie Abmessungen, einen Rahmen, der eine leere Flächeumschließt, und kann auf dem Bildschirm auf einer bestimmten Position angezeigt werden.Von der Basisklasse wird nun eine Klasse abgeleitet, die die Eigenschaften des einfachenleeren Fensters mit Rahmen übernimmt und dahingehend erweitert eine Schaltfläche mitwahlfreiem Text, z.B. „OK“, darzustellen. Mit der auf diese Weise geschaffenen neuenKlassen können nun alle möglichen Schaltflächen mit den gewünschten Abmessungen unddem erforderlichen Text erzeugt und auf dem Bildschirm positioniert werden. (die VCL vonDelphi wurde nach diesem Prinzip entwickelt)

Dieses Beispiel zeigt auch einen weiteren Vorteil von OOP. Es ist nicht erforderlich, diekomplexe Programmierung zur Erzeugung eines Fensters nachvollziehen. Um die abgeleitetKlasse zu bilden, sind nähere Kenntnisse über den Programmcode der Basisklasse nichterforderlich. Es ist hinreichend, die Eigenschaften der Basisklasse und die Verfahren, dieseEigenschaften anzusprechen, zu kennen.

22..22..33 PPoollyymmoorrpphhiieePolymorphie bedeutet soviel wie „Vielgestaltigkeit“ und hängt eng mit der Vererbungzusammen. Bei der reinen Vererbung enthalten abgeleitete Klassen alle Eigenschaften derBasisklasse und erweitern diese um zusätzliche, eigene, Eigenschaften. Wenn die Basisklasseentsprechend programmiert wurde, können Ableitungen dieser Klasse bestimmteEigenschaften der Basisklasse nicht nur übernehmen sondern völlig verändern.

Hierzu wird das Beispiel von vorher erweitert. Nach der oben beschriebenen Vererbung hatdie Klasse zur Darstellung der Schaltflächen nur den Text in ein kleines Fenster eingefügt.Eine Schaltfläche macht aber einen optisch viel besseren Eindruck, wenn sie erhobendargestellt wird. Dieser visuelle Eindruck kann mit einem anderen Rahmen um dieSchaltfläche erzeugt werden. Die von der Basisklasse ererbte Eigenschaft, einen Rahmen umein Fenster zu zeichnen, wird in der abgeleiteten Klasse verdeckt und durch die Eigenschaft,einen Rahmen mit 3D-Effekt zu zeichnen ersetzt. Die von der abgeleiteten Klasse erzeugtenSchaltflächen werden nun alle mit dem neuen 3D-Rahmen dargestellt.

33 OObbjjeekkttee

33..11 WWaass iisstt eeiinn OObbjjeekktt??Ein Objekt in der Softwareentwicklung ist letztlich dasselbe wie in der realen Welt. (z.B.Auto). Das Objekt hat gewisse Eigenschaften (z.B. Farbe, aktuelle Geschwindigkeit,Höchstgeschwindigkeit, Gang, ...) und das Objekt kann bestimmte Tätigkeiten bzw. Befehleausführen (z.B. Gas geben, bremsen, ...).

Page 6: Objektorientierte Programmierung in Delphi

Spezialgebiet – Informatik Manuel PöterOOP in Delphi GRG XV Auf der Schmelz

3

Der Programmierer versteht unter einem Objekt die Zusammenfassung (Kapselung) vonDaten und den zugehörigen Funktionalitäten. Das Konzept der objektorientiertenProgrammierung (OOP) überwindet den prozeduralen Ansatz der klassischen Sprachenzugunsten einer realitätsnahen Modellierung.

Prozedurale Programmierung bedeutet, dass man eine Reihe von Prozeduren und Funktionen,mit oder ohne Parameter hat, und diese vom Hauptprogramm aus aufgerufen werden. DasKonzept von OOP schließt jetzt mehrere solche Prozeduren zusammen und verpackt sie in einsogenanntes „Objekt“. Prozeduren die einem Objekt zugeordnet sind nennt man Methoden.

Ein Objekt besteht also aus Methoden (Prozeduren und Funktionen) und Attributen (Variablendie Daten enthalten).

33..22 AAttttrriibbuuttee uunndd MMeetthhooddeennDer Begriff Objektfelder umfasst alle Variablen und Funktionen die von einem Objektgekapselt werden. Wie bereits erwähnt nennt man solche Funktionen Methoden und dieVariablen Attribute.

Während Attribute den Zustand oder die Eigenschaften eines Objektes beschreiben,entsprechen die Methoden seinem Verhalten. Ein Objekt kann lediglich über eine Schnittstellemit einem anderen Objekt kommunizieren, indem es dessen öffentliche (public) Methodenbenutzt. Methoden sind Aktionen, die das Objekt durchführen kann.

Die Attribute können dabei durch die sogenannten Zugriffsmethoden sozusagen geschütztwerden. D.h. dass nur über diese Methoden auf ein Attribut zugegriffen werden kann.

Bei den Attributen kann man unterscheiden zwischen Eigenschaften und Ereignissen.Eigenschaften repräsentieren die Daten, die ein Objekt enthält. Ereignisse sind Bedingungen,auf die das Objekt reagieren kann.

Page 7: Objektorientierte Programmierung in Delphi

Spezialgebiet – Informatik Manuel PöterOOP in Delphi GRG XV Auf der Schmelz

4

OOBBJJEEKKTTEE IINN DDEELLPPHHII

11 DDiiee KKllaasssseennddeekkllaarraattiioonnDamit überhaupt erst mal ein Objekt entstehen kann muss man eine Klasse deklarieren. InDelphi ist eine solche Klassendeklaration wie folgt aufgebaut:

type KlassenName = class(Basisklasse) private { private Deklarationen } protected { protected Deklarationen } public { public Deklarationen } published { published Deklarationen } end;

Für KlassenName ist dabei der Name der neuen Klasse und für Basisklasse der Klassennamedes Vorfahren einzutragen.

Hinweis: In Delphi hat es sich eingebürgert alle Klassennamen mit einem großen „T“ zubeginnen. Diese Namensgebung mit vorangestelltem Präfix entstammt ursprünglichder ungarischen Notation. Es ist zwar keine Pflicht sich an diese Konvention zuhalten, aber es ist trotzdem anzuraten, vor allem wenn diese Objekte veröffentlichtoder anderen Programmieren zur Verfügung gestellt werden.

Mit dem Schlüsselwort „type“ wird eine Typendeklaration eingeleitet. Dazu gehören unteranderem Records und Klassen. Wird keine Basisklasse angegeben, so wird das Objektautomatisch von TObject abgeleitet (siehe auch Kapitel 2 - Aller Anfang ist TObject).

22 AAlllleerr AAnnffaanngg iisstt TTOObbjjeeccttIn Delphi sind alle Objekte automatisch vom Basisobjekt TObject abgeleitet – es istsozusagen der Urahn aller VCL-Objekte und Komponenten. TObject ist folgendermaßendeklariert:

{ TObject class }type TObject = class constructor Create; procedure Free; class function InitInstance(Instance: Pointer): TObject; procedure CleanupInstance; function ClassType: TClass; class function ClassName: ShortString; class function ClassNameIs(const Name: String): Boolean; class function ClassParent: TClass; class function ClassInfo: Pointer; class function InstanceSize: Longint; class function InheritsFrom(AClass: TClass): Boolean; class function MethodAddress(const Name: ShortString): Pointer;

Page 8: Objektorientierte Programmierung in Delphi

Spezialgebiet – Informatik Manuel PöterOOP in Delphi GRG XV Auf der Schmelz

5

class function MethodName(Address: Pointer): ShortString; function FieldAddress(const Name: ShortString): Pointer; function GetInterface(const IID: TGUID; out Obj): Boolean; class function GetInterfaceEntry(const IID: TGUID): PInterfaceEntry; class function GetInterfaceTable: PInterfaceTable; function SafeCallException(ExceptObject: TObject; ExceptAddr: Pointer): HResult; virtual; procedure AfterConstruction; virtual; procedure BeforeDestruction; virtual; procedure Dispatch(var Message); virtual; procedure DefaultHandler(var Message); virtual; class function NewInstance: TObject; virtual; procedure FreeInstance; virtual; destructor Destroy; virtual; end;

TObject kapselt das grundlegende Verhalten, das allen Objekten in der VCL gemeinsam ist.Mit den von TObject eingeführten Methoden kann man:

- Objektinstanzen erzeugen, verwalten und auflösen. Dies geschieht durch Zuweisen,Initialisieren und Freigeben von Speicher für das Objekt.

- auf objektspezifische Informationen über den Klassentyp und die Instanz zugreifenund Laufzeitinformationen von Eigenschaften verwalten, die als published deklariertsind.

- Botschaften individuell bearbeiten.

- Schnittstellen, die das Objekt implementiert, unterstützen.

TObject wird als direkte Basis für einfache Objekte verwendet, die nicht persistent sind (alsonicht gespeichert und erneut geladen werden müssen) und die keinen anderen Objektezugewiesen werden. Wenn bei der Deklaration einer neuen Objektklasse kein Vorfahrangegeben wird, setzt Delphi als Vorfahr automatisch die Klasse TObject ein.

Die Leistungsfähigkeit von Delphi-Objekten beruht zu einem großen Teil auf den Methoden,die TObject einführt. Viele dieser Methoden sind für die interne Verwendung in der Delphi-Umgebung vorgesehen, nicht aber für einen direkten Aufruf durch den Anwender. AndereMethoden müssen in abgeleiteten Objekten und Komponenten, die ein komplexeres Verhaltenzeigen, überschrieben werden.

33 KKaappsseelluunnggWie bereits erwähnt versteht man unter Kapselung das Verbergen der inneren Struktur einesObjekts. Die internen Daten und einige verborgene Methoden sind geschützt, also von außennicht zugänglich. Die Manipulation des Objektes kann lediglich über streng definierte, überdie Schnittstelle zur Verfügung gestellte, öffentliche Methoden erfolgen. In diesemZusammenhang trifft man in jedem Fall auf die beiden Schlüsselwörter public und private:

33..11 pprriivvaatteeAuf ein private-Element kann nur innerhalb des Moduls (Unit oder Programm) zugegriffenwerden, in dem die Klasse deklariert ist. Mit anderen Worten - eine private-Methode kannnicht von anderen Modulen aufgerufen werden, und als private deklarierte Felder oderEigenschaften können nicht von anderen Modulen gelesen oder geschrieben werden. Indem

Page 9: Objektorientierte Programmierung in Delphi

Spezialgebiet – Informatik Manuel PöterOOP in Delphi GRG XV Auf der Schmelz

6

man wandte Klassendeklarationen im selben Modul zusammenfassen, man diesen Klassenalso den Zugriff auf alle private-Elemente ermöglichen, ohne die Elemente anderen Modulenbekanntzumachen.

33..22 ppuubblliiccEin public-Element unterliegt keinerlei Zugriffsbeschränkungen. Es ist überall dort sichtbar,wo auf seine Klasse verwiesen werden kann. Die Benutzerschnittstelle einer Klasse stellt sichdurch Elemente dar, die mit public vereinbart wurden.

33..33 pprrootteecctteeddNeben private und public gibt es noch ein drittes Schlüsselwort: protected

Ein protected-Element ist innerhalb des Moduls mit der Klassendeklaration und in allenabgeleiteten Klassen (unabhängig davon, in welchem Modul sie deklariert sind) sichtbar. Esist sozusagen ein „Mittelding“ zwischen private und public. Mit anderen Worten: auf einprotected-Element können alle Methoden einer Klasse zugreifen, die von der Klasse mit derElementdeklaration abgeleitet ist. Mit diesem Sichtbarkeitsattribut werden also Elementedeklariert, die nur in den Implementierungen abgeleiteter Klassen verwendet werden sollen.Vor dem Zugriff von außen sind diese Elemente geschützt.

33..44 ppuubblliisshheeddDer Vollständigkeit halber sollte hier auch noch das vierte Schlüsselwort published erwähntwerden.

published-Elemente haben dieselbe Sichtbarkeit wie public-Elemente. Im Unterschied zudiesen werden jedoch für published-Elemente Laufzeit-Typinformationen generiert. Sieermöglichen einer Anwendung, die Felder und Eigenschaften eines Objekts dynamischabzufragen und seine Methoden zu lokalisieren. Delphi verwendet diese Informationen, umbeim Speichern und Laden von Formulardateien (DFM) auf die Werte von Eigenschaftenzuzugreifen, Eigenschaften im Objektinspektor anzuzeigen und spezielle Methoden(sogenannte Ereignisbehandlungsroutinen) bestimmten Eigenschaften (den Ereignissen)zuzuordnen.

published-Eigenschaften sind auf bestimmte Datentypen beschränkt. Ordinal–, String–,Klassen–, Schnittstellen– und Methodenzeigertypen können mit dieser Sichtbarkeit deklariertwerden. Bei Mengentypen ist dies nur möglich, wenn die Ober- und Untergrenze desBasistyps einen Ordinalwert zwischen 0 und 31 hat (die Menge muß also in ein Byte, Wortoder Doppelwort passen). Auch alle Real-Typen außer Real48 können als published deklariertwerden. Für Array-Eigenschaften kann dieser Gültigkeitsbereich nicht angegeben werden.

Obwohl alle Methoden als published deklariert werden können, sind in einer Klasse nichtzwei oder mehr überladene published-Methoden mit demselben Namen erlaubt. Felderkönnen nur mit dieser Sichtbarkeit angegeben werden, wenn sie einen Klassen- oderSchnittstellentyp haben.

44 MMeetthhooddeennGrundsätzlich gibt es drei verschiedene Arten von Methoden:

Page 10: Objektorientierte Programmierung in Delphi

Spezialgebiet – Informatik Manuel PöterOOP in Delphi GRG XV Auf der Schmelz

7

44..11 SSttaattiisscchheeMethoden sind standardmäßig statisch. Statische Methoden können in abgeleiteten Klassennicht überschrieben werden. Wenn in der abgeleiteten Klassen eine Methode mit demselbenNamen deklariert wird, so wird die Methode der Basisklasse lediglich verdeckt, nicht jedochüberschrieben. Beim Aufruf bestimmt der deklarierte Typ (also der Typ zur Compilierzeit)der im Aufruf verwendeten Klassen- bzw. Objektvariable, welche Implementierung aktiviertwird.

44..22 VViirrttuueelllleeVirtuelle Methoden können im Gegensatz zu statischen Methoden in abgeleiteten Klassenüberschrieben werden. Beim Aufrufen einer überschriebenen Methode bestimmt nicht derdeklarierte, sondern der aktuelle Typ (also der Typ zur Laufzeit) der im Aufruf verwendetenKlassen- bzw. Objektvariable, welche Implementierung aktiviert wird.

44..33 DDyynnaammiisscchheeVirtuelle und dynamische Methoden sind von der Semantik her identisch – sie können beidein abgeleiteten Klassen überschrieben werden. Sie unterscheiden sich nur bei derImplementierung der Aufrufverteilung zur Laufzeit. Virtuelle .Methoden werden aufGeschwindigkeit, dynamische Methoden auf Code-Größe optimiert.

Im Allgemeinen kann mit virtuellen Methoden polymorphes Verhalten am effizientestenimplementiert werden. Dynamische Methoden sind hilfreich, wenn in einer Basisklasse einegroße Anzahl überschreibbarer Methoden deklariert ist, die von vielen abgeleiteten Klassengeerbt, aber nur selten überschrieben werden.

44..44 BBoottsscchhaaffttssvveerraarrbbeeiitteennddee MMeetthhooddeennDiese Methodenart passt zwar nicht in das Schema der vorherigen drei, ist aber auch einespezielle Methodenart und wird deshalb in ebenfalls in diesem Kapitel beschrieben.

Um den Sinn dieser Methodenart zu verstehen muss man ein bisschen die Hintergründe undArbeitsweise von Windows kennen. Windows ist ein sogenanntes nachrichtenbasiertesBetriebssystem. Ich gehe hier nicht auf Details ein sondern erkläre nur kurz ein bisschen dasPrinzip.

44..55 HHiinntteerrggrruunndd –– DDiiee AArrbbeeiittsswweeiissee vvoonn WWiinnddoowwssWenn z.B. mit der Maus geklickt wird, oder eine Taste der Tastatur gedrückt wird, so wirdvon Windows eine entsprechende Message erzeugt. Wenn die linke Maustaste gedrückt wirdist dies WM_LBUTTONDOWN und wenn sie wieder losgelassen wird, wird entsprechenddie WM_LBUTTONUP Nachricht erzeugt. Der Präfix WM steht dabei für Windows-Message. Sobald diese Nachricht erzeugt wurde wird sie an das betreffende Fenster geschickt.Bei einem Mausklick ist das betroffene Fenster also jenes über dem sich die Maus zu diesemZeitpunkt befindet. (Wichtig: In Windows sind alle Steuerelemente „Fenster“ und könnendaher Nachrichten empfangen!) Um nun eine solche Nachricht empfangen zu können hatjedes Fenster eine Message-Loop und eine Fensterprozedur. In der Message-Loop werden dieeintreffenden Nachrichten abgelegt und in der Fensterprozedur werden sie ausgelesen undverarbeitet. Über eine Botschaftsverarbeitende Methode kann man nun auf solcheeintreffenden Nachrichten reagieren. Voraussetzung für eine BotschaftsverarbeitendeMethode ist ein gültiges Fensterhandle und eine korrekte Fensterprozedur. Entweder

Page 11: Objektorientierte Programmierung in Delphi

Spezialgebiet – Informatik Manuel PöterOOP in Delphi GRG XV Auf der Schmelz

8

implementiert man diese Punkte selbst direkt über das Win32-API oder aber man leitet seinObjekt von TWinControl ab. Dieses Objekt implementiert bereits alle wichtigen Aspekte.

44..66 IImmpplleemmeennttiieerruunngg –– SScchhllüüsssseellwwoorrtt mmeessssaaggeeEine Botschaftsverarbeitende Methode wird mit dem Schlüsselwort message und demdarauffolgenden Message-Namen gekennzeichnet.

Beispiel:

type TMyWinControl = class(TWinControl) protected procedure WMLButtonDown(Msg: TMessage); message WM_LBUTTONDOWN; end;

Alle botschaftsverarbeitenden Methoden brauchen einen Parameter über den man an dieInformationen der Nachricht kommt. Da alle Messages gleich aufgebaut sind gibt es so etwaswie einen „Universal-Record“. Dieser ist eben TMessage. D.h. jede botschaftsverarbeitendeMethode kann einen Parameter vom Typ TMessage haben. Neben TMessage sind der UnitMessages noch andere nachrichtenspezifische Records definiert über die eine einfachereAbfrage der Informationen möglich ist.

55 KKoonnssttrruukkttoorr uunndd DDeessttrruukkttoorrEine objektorientierte Sprache wie Delphi realisiert das Erzeugen und Entfernen von Objektenmit sogenannten Konstruktoren und Destruktoren. Beides sind spezielle Methoden innerhalbder Klassendeklaration. Der Konstruktor ist dafür verantwortlich, dass überhaupt erst einObjekt entstehen kann. Er wird bei der Initialisierung, also bei der Erzeugung, eines Objektesaufgerufen und führt alle zur Initialisierung nötigen Aufgaben wie z.B. Allozierung vonSpeicher und initialisieren der Objektfelder aus. Der Destruktor wird beim Entfernen vonObjekten aufgerufen und ist für sämtliche Aufräumarbeiten wie z.B. freigeben vonalloziertem Speicher verantwortlich.

55..11 OObbjjeekkttvvaarriiaabblleenn uunndd IInnssttaannzzeennUm mit Objekten arbeiten zu können, muss eine Objektvariable als sogenannte Instanz derKlasse gebildet werden. Im Gegensatz zu normalen Variablendeklarationen ist es hier mit derreinen Deklaration noch nicht getan sondern bedarf folgender drei Schritte:

• Mit var wird zunächst eine Referenz auf ein Objekt des gewünschten Typsdeklariert. Dies ist ein Zeiger, der momentan noch den Wert nil hat, alsogewissermaßen „in die Luft“ oder auch „nirgendwo“ hinzeigt.

• Durch Aufruf des sogenannten Konstruktors (meist ist dies die Create-Methode)wird eine Instanz der Klasse gebildet. Erst jetzt entsteht das eigentliche Objekt undder im ersten Schritt deklarierte Zeiger (Referenz) verweist auf einen konkretenSpeicherbereich.

• Wenn das Objekt nicht mehr benötigt wird, sollte der von ihm belegte Speicherwieder freigegeben werden. Dies geschieht durch den Aufruf eines sogenanntenDestruktors (meist ist dies die Free-Methode). Um Missverständnisse vorzubeugensollte nach Aufruf des Destruktors der Objektvariablen wieder der Wert nil

Page 12: Objektorientierte Programmierung in Delphi

Spezialgebiet – Informatik Manuel PöterOOP in Delphi GRG XV Auf der Schmelz

9

zugewiesen werden, damit nicht auf das nicht mehr vorhandene Objekt und damitauf nicht reservierten Speicher zugegriffen wird.

Beispiel:

var MyObject: TMyObject;begin MyObject := TMyObject.Create; // MyObject muss erst instanziert werden .... // arbeiten mit MyObject MyObject.Free; // Objekt wieder freigebenend;

Die Instanz eines Objektes ist ein dynamisch reservierter Speicherbereich mit einer internenStruktur, die vom Objekttyp abhängt. Jedes Objekt besitzt eine mit eigenen Werten gefüllteKopie der im Objekttyp deklarierten Felder, benutzt aber die Methoden unverändert mitanderen Objekten zusammen.

Ein Objekt ist lediglich eine Referenz auf ein bestimmtes Objekt der Klasse. Sie enthält alsonicht bereits das Objekt, sondern stellt einen Zeiger auf den Speicherbereich des Objektesbereit. Diese Zeiger werden, im Gegensatz zu den in C++ üblichen Objekten, von Delphiintern dereferenziert. Es können sich so mehrere Objektvariablen auf ein und dieselbe Instanzbeziehen. Wenn eine Objektvariable den Wert nil enthält, so bedeutet das, dass sie momentankein Objekt referenziert.

55..22 DDeerr KKoonnssttrruukkttoorrDie Deklaration eines Konstruktors gleicht einer normalen Prozedurdeklaration, beginnt abermit dem Wort constructor.

Beispiel:

type TMyObject = class(TObject) public constructor Create; override; // der Konstruktor Info: String; // Variable die im Konstruktor initialisiert wird end;

Der Implementations-Teil schaut dann so aus:

constructor TMyObject.Create;begin inherited; Info := ‘Dieser Text wurde im Konstruktor zugewiesen’end;

Die beiden Schlüsselwörter override und inherited werden im Kapitel 6 – „Überschreibenvon Methoden“ noch beschrieben.

Eine Klasse kann durchaus mehrere Konstruktoren deklarieren bzw. implementieren. Diesemüssen dann eventuell unterschiedliche Methodennamen haben oder mit der Direktiveoverload gekennzeichnet sein (siehe Kapitel 7 – Überladen von Methoden).

Page 13: Objektorientierte Programmierung in Delphi

Spezialgebiet – Informatik Manuel PöterOOP in Delphi GRG XV Auf der Schmelz

10

55..33 DDeerr DDeessttrruukkttoorrEin Destruktor ist eine spezielle Methode, die ein Objekt im Speicher freigibt. DieDeklaration eines Destruktors gleicht, genauso wie schon die des Konstruktors, der einerProzedur, beginnt aber dem Namen entsprechend mit dem Wort destructor.

Destruktoren müssen die Standardaufrufkonvention register verwenden. Obwohl in einerKlasse mehrere Destruktoren implementiert werden können, ist es ratsam, nur die geerbteDestroy-Methode zu überschreiben und keine weiteren Destruktoren zu deklarieren.

Beispiel:

type TMyObject = class(TObject) public destructor Destroy; override; // der Destruktor end;

Der Implementations-Teil schaut dann so aus:

destructor TMyObject.Destroy;begin .... // alle notigen Freigaben und ähnliches inherited;end;

Die beiden Schlüsselwörter override und inherited werden im Kapitel 6 – „Überschreibenvon Methoden“ noch beschrieben.

Ein Destruktor kann nur über ein Instanzobjekt aufgerufen werden.

MyObject.Destroy;

Beim Aufruf eines Destruktors werden zuerst die in der Implementierung angegebenenAktionen ausgeführt. Normalerweise werden hier untergeordnete Objekte und zugewieseneRessourcen freigegeben. Danach wird das Objekt im Speicher freigegeben.

Wenn beim Erstellen eines Objekts eine Exception auftritt, wird das unvollständige Objektautomatisch durch einen Aufruf von Destroy freigegeben. Der Destruktor muß daher auch inder Lage sein, Objekte freizugeben, die nur teilweise erstellt wurden. Da im Konstruktor alleFelder eines neuen Objekts mit Null initialisiert werden, haben Klassenreferenz- undZeigerfelder in einer unvollständigen Instanz immer den Wert nil. Man sollte solche Felder imDestruktor immer auf den Wert nil testen, bevor man Operationen mit ihnen durchführen.Wenn man Objekte nicht mit Destroy, sondern mit der Methode Free (von TObject)freigeben, wird diese Prüfung automatisch durchgeführt.

66 ÜÜbbeerrsscchhrreeiibbeenn vvoonn MMeetthhooddeennUm eine Methode zu überschreiben, braucht sie nur mit der Direktiven override erneutdeklariert zu werden. Dabei müssen Reihenfolge und Typ der Parameter sowie der Typ desRückgabewertes (falls vorhanden) mit der Deklaration in der Basisklasse übereinstimmen.

Im folgenden Beispiel wird die in der Klasse TFigure deklarierte Methode Draw in zweiabgeleiteten Klassen überschrieben:

Page 14: Objektorientierte Programmierung in Delphi

Spezialgebiet – Informatik Manuel PöterOOP in Delphi GRG XV Auf der Schmelz

11

type TFigure = class procedure Draw; virtual; end;

TRectangle = class(TFigure) procedure Draw; override; end;

TEllipse = class(TFigure) procedure Draw; override; end;

Ausgehend von diesen Deklarationen zeigt der folgende Programmcode, wie sich der Aufrufeiner virtuellen Methode durch eine Variable auswirkt, deren aktueller Typ zur Laufzeitgeändert wird.

var Figure: TFigure;begin Figure := TRectangle.Create; Figure.Draw; // Ruft TRectangle.Draw auf. Figure.Destroy;

Figure := TEllipse.Create; Figure.Draw; // Ruft TEllipse.Draw auf. Figure.Destroy;end;

66..11 AAuuffrruuff ggeeeerrbbtteerr MMeetthhooddeenn –– iinnhheerriitteeddIn überschriebenen Methoden ist es oft notwendig die alte Verhaltensweise des Objektesbeizubehalten und nur neue Initialisierungen oder Reaktionen einzubauen. Um nicht dengesamten Code „nachbauen“ zu müssen gibt es in Delphi die Möglichkeit dieOriginalmethode der Basisklasse aufzurufen.

Das reservierte Wort inherited ist für die Polymorphie von großer Bedeutung. Es kann inMethodenimplementierungen (mit oder ohne nachfolgenden Bezeichner) angegeben werden.

Folgt auf inherited ein Methodenbezeichner, entspricht dies einem normalen Methodenaufruf.Der einzige Unterschied besteht darin, daß die Suche nach der Methode bei dem direktenVorfahren der Klasse beginnt, zu der die Methode gehört. Durch die folgende Anweisungwird beispielsweise die geerbte Methode Create aufgerufen:

inherited Create(...);

Die Anweisung inherited ohne Bezeichner verweist auf die geerbte Methode mit demselbenNamen wie die aufrufende Methode. In diesem Fall kann inherited mit oder ohne Parameterangegeben werden. Ohne Parameterangabe werden der geerbten Methode einfach dieParameter der aufrufenden Methode übergeben. So wird beispielsweise

inherited;

Page 15: Objektorientierte Programmierung in Delphi

Spezialgebiet – Informatik Manuel PöterOOP in Delphi GRG XV Auf der Schmelz

12

häufig in der Implementierung von Konstruktoren verwendet. Der geerbte Konstruktor wirdalso mit den Parametern aufgerufen, die an die abgeleitete Klasse übergeben wurden.

In einem Konstruktor sollte inherited immer als erstes aufgerufen werden, damit alleInitialisierungen des Basisobjekts fertig sind, bevor man selbst mit dem Objekt arbeitet. ImDestruktor ist es dafür genau umgekehrt. Hier sollten zuerst alle eigenen Speicher bzw.Objektfreigaben stattfinden bevor man über inherited den Destruktor des Basisobjektesaufruft.

77 ÜÜbbeerrllaaddeenn vvoonn MMeetthhooddeennNur virtuelle und dynamische Methoden können überschrieben werden, es können jedoch alleMethoden überladen werden. Unter dem „überladen von Methoden“ versteht man, dass eineKlasse mehrere Methoden mit demselben Methodennamen deklarieren kann. Diese Methodenmüssen sich jedoch entweder in Parameteranzahl oder Parameterart unterscheiden. DerCompiler sucht dann beim kompilieren automatisch die passende Methode aus.

Um eine Methode zu überladen muss sie mit der Anweisung overload neu deklariert werden.Wenn sich die Parameterangaben von denen ihres Vorfahren unterscheiden, wird die geerbteMethode überladen, ohne daß sie dadurch verdeckt wird. Bei einem Aufruf der Methode ineiner abgeleiteten Klasse wird dann diejenige Implementierung aktiviert, bei der dieParameter übereinstimmen.

88 FFeellddeerr uunndd EEiiggeennsscchhaafftteennEigenschaften (Properties) und Felder eines Objektes werden gemeinsam auch als dessenAttribute bezeichnet.

Felder belegen „normale“ Speicherplätze wie z.B. die Felder eines Records und könnendemnach direkt gelesen bzw. geändert/gesetzt werden.

Objekteigenschaften werden mit dem Schlüsselwort property deklariert und erscheinen nachaußen wie normale Objektfelder, allerdings kapseln sie intern nur Methoden zum Lesen(read) bzw. Schreiben (write) des Wertes eines oder mehrerer Felder und speichern selbstkeine Werte. Diese mit read bzw. write spezifizierten Methoden nennt man Zugriffsmethoden.

88..11 ZZuuggrriiffffssmmeetthhooddeennZugriffsmethoden sind dazu da, um Objektfelder von außen zu schützen (z.B. ReadOnly-Eigenschaften) oder Zuweisungen einer Gültigkeitskontrolle zu unterziehen. DurchWeglassen von write lässt sie beispielsweise der Schreibzugriff verwehren. Zugriffsmethodenwerden meist entweder als public oder aber als protected deklariert. Die Deklaration alsprotected hat den Vorteil, dass abgeleitete Klassen die Methode den Umständen entsprechendanpassen bzw. verändern kann (vorausgesetzt sie wurde als virtual oder dynamic deklariert).

Die Deklaration einer Eigenschaft muß einen Namen, einen Typ und mindestens eineZugriffsangabe enthalten. Die Syntax lautet folgendermaßen:

property Eigenschaftsname[Indizes]:Typ indexInteger-Konstante Bezeichner;

• Eigenschaftsname ist ein beliebiger gültiger Bezeichner.

• [Indizes] ist optional und besteht aus einer Folge von durch Semikolons getrenntenParameterdeklarationen. Jede Deklaration hat die Form Bezeichner1 ...

Page 16: Objektorientierte Programmierung in Delphi

Spezialgebiet – Informatik Manuel PöterOOP in Delphi GRG XV Auf der Schmelz

13

Bezeichnern: Typ. Weitere Informationen hierzu finden Sie im Abschnitt Array-Eigenschaften.

• Die Klausel index Integer-Konstante ist optional (siehe Indexangaben).

• Bezeichner ist eine Folge von read, write, stored, default (oder nodefault) undimplements-Angaben. Jede Eigenschaftsdeklaration muß zumindest einen read-oder write-Bezeichner enthalten.

Eigenschaften werden durch ihre Zugriffsangaben definiert. Sie können im Gegensatz zuFeldern nicht als var-Parameter übergeben oder mit dem Adreßoperator @ versehen werden.Der Grund dafür liegt darin, daß eine Eigenschaft nicht notwendigerweise im Speichervorhanden sein muß. Sie kann beispielsweise eine read-Methode haben, die einen Wert auseiner Datenbank abruft oder einen Zufallswert generiert.

Beispiel:

type TAuto = class(TObject) private FTempo: Integer; public constructor Create; procedure Gasgeben; procedure Bremsen; property Geschwindigkeit: Integer read FTempo; end;

{ implementation }constructor TAuto.Create;begin inherited; FTempo := 0;end;

procedure TAuto.Gasgeben;begin Inc(FTempo, 5);end;

procedure TAuto.Bremsen;begin Dec(FTemp, 5);end;

Das Objekt Auto implementiert einen Konstruktor, zwei Methoden und eine Eigenschaft. ImKonstruktor wird dafür gesorgt, dass die Startgeschwindigkeit 0 ist.

99 EErreeiiggnniisssseeEreignisse sind eine Sonderform von Eigenschaften. Um das Prinzip von Ereignissen zuverstehen ist allerdings ein gutes Verständnis von Pointern erforderlich.

Jede Funktion oder Prozedur ist eigentlich nur ein Block im Arbeitsspeicher. Daher kann manmit einem Pointer mit der Adresse auf diese Speicherstelle auch einen Pointer auf eine

Page 17: Objektorientierte Programmierung in Delphi

Spezialgebiet – Informatik Manuel PöterOOP in Delphi GRG XV Auf der Schmelz

14

Funktion/Prozedur haben. Diese Pointer nennt man Prozedurzeiger. Und genau nach diesemPrinzip funktionieren Ereignisse. Ein Ereignis ist nur ein Pointer auf eine Prozedur, die beider Auslösung des Ereignisses aufgerufen wird. Um aber so ein Ereignis zu implementierenbedarf es ein paar Vorbereitungen.

99..11 PPrroozzeedduurraallee TTyyppeennDefinieren wir mal eine Funktion mit dem Namen Calc, die zwei Integer-Parameterübernimmt und einen Integer-Wert zurückgibt:

function Calc(X, Y: Integer): Integer;

Man kann die Calc-Funktion nun der Variablen F zuweisen und aufrufen:

var F: function(X, Y: Integer): Integer; Ergebnis: Integer;begin F := Calc; Ergebnis := F(3, 5);end;

Der Bezeichner hinter dem Schlüsselwort procedure bzw. function ist der Name desprozeduralen Typs. Dieser Typname kann direkt in einer Variablendeklaration (siehe oben)verwendet oder zur Deklaration neuer Typen benutzt werden:

type TIntegerFunction = function: Integer; TProcedure = procedure; TStrProc = procedure(const S: string); TMathFunc = function(X: Double): Double;var F: TIntegerFunction; { F ist eine parameterlose Funktion, die einen Integer zurückgibt }

Proc: TProcedure; { Proc ist eine parameterlose Prozedur }

SP: TStrProc; { SP ist eine Prozedur, die einen String- Parameter übernimmt }

M: TMathFunc; { M ist eine Funktion, die einen Double-Parameter (reeller Wert) übernimmt und einen Double-Wert zurückgibt }

Diese Typen können beliebig eingesetzt werden. z.B. so:

procedure FuncProc(P: TIntegerFunction); { FuncProc ist eine Prozedur, deren einziger Parameter eine parameterlose Funktion ist, die einen Integer zurückgibt }

99..22 MMeetthhooddeennzzeeiiggeerrDie Deklaration von Methodenzeigern unterscheidet sich nur geringfügig von der vonProzedurzeigern. Um eine Methode eines Instanzobjekts zur , muss dem Namen desprozeduralen Typs die Klausel of object hinzugefügt werden:

Page 18: Objektorientierte Programmierung in Delphi

Spezialgebiet – Informatik Manuel PöterOOP in Delphi GRG XV Auf der Schmelz

15

type TMethod = procedure of object; TNotifyEvent = procedure(Sender: TObject) of object;

Ein Methodenzeiger wird in Form zweier Zeiger codiert, von denen der erste die Adresse derMethode speichert. Der zweite enthält eine Referenz auf das Objekt, zu dem die Methodegehört.

Beispiel:

type TNotifyEvent = procedure(Sender: TObject) of object; TMainForm = class(TForm) procedure ButtonClick(Sender: TObject); ... end;

var MainForm: TMainForm; OnClick: TNotifyEvent;

Nach diesen Deklarationen ist die folgende Zuweisung korrekt:

OnClick := MainForm.ButtonClick;

Zwei prozedurale Typen sind kompatibel, wenn sie folgende Bedingungen erfüllen:

• Sie verwenden dieselbe Aufrufkonvention.

• Sie haben denselben (oder keinen) Rückgabetyp.

• Sie verwenden eine identische Anzahl von Parametern. Die Parameter an einerbestimmten Position müssen identische Typen haben (die Parameternamen spielenkeine Rolle).

Zeiger auf Prozeduren sind niemals kompatibel zu Zeigern auf Methoden. Der Wert nil kannjedem prozeduralen Typ zugewiesen werden.

99..33 AAuussllöösseenn vvoonn EErreeiiggnniisssseennUm ein Ereignis auszulösen reicht ein einfacher Aufruf über den Methodenzeiger. Vor demAufruf muss allerdings überprüft werden, ob der Zeiger nicht den Wert nil hat, denn dannsonst könnte es zu potentiellen Zugriffsfehlern kommen. Eine typische Deklaration bzw.Implementation von Ereignissen inklusive Aufruf sieht etwa so aus:

type TMyObject = class(TObject) private .... FOnClick: TNotifyEvent; public .... procedure Click; property OnClick: TNotifyEvent read FOnClick write FOnClick; end;

Page 19: Objektorientierte Programmierung in Delphi

Spezialgebiet – Informatik Manuel PöterOOP in Delphi GRG XV Auf der Schmelz

16

procedure TControl.Click;begin if Assigned(FOnClick) then FOnClick(Self);end;

1100 KKllaasssseennmmeetthhooddeennEine Klassenmethode arbeitet nicht mit Objekten sondern mit Klassen. Für den Aufruf einerKlassenmethode ist deshalb keine Objektvariable sondern lediglich die Klassendeklarationund Implementierung notwendig. Deklaration und Implementierung müssen mit demreservierten Wort class eingeleitet werden.

Beispiel:

type TMyObject = class(TMyObject) public class function VersionsInfo: String; end;

{ Implementation }

class function TMyObject.VersionsInfo: String;begin Result := ‘Version 1.0 der Beispielklasse für Klassenmethoden’;end;

Klassenmethoden sind von Anfang an im Speicher vorhanden – im Gegensatz zu Objekten,die erst durch einen Konstruktor ins Leben gerufen werden müssen. Deshalb darf man voneiner Klassenmethode natürlich nicht auf Objektfelder der Klasse zugreifen, da kein Objektund damit auch keine Objektfelder existieren. Würde der Compiler einen solchen Zugriffnicht schon zur Entwurfszeit als Fehler erkennen und markieren, würde es zur Laufzeit zupotentiellen Zugriffsfehlern kommen.

1111 FFoorrwwaarrdd--DDeekkllaarraattiioonneennManchmal ist es nötig, dass zwei unterschiedliche Klassen die jeweils andere referenziert:

type TFirstClass = class(TObject) public Second : TSecondClass; end;

TSecondClass = class(TObject) public First : TFirstClass; end;

Allerdings ahndet der Compiler diese Deklaration beinhart mit einer Fehlermeldung, da in derDeklaration von TMyClass1 noch nichts von der Klasse TMyClass2 bekannt ist. Um diesesProblem zu umgehen setzt man Forward-Deklarationen, auf Deutsch Vortwärtsdeklarationen,ein. Forward-Deklarationen sehen folgendermaßen aus:

Page 20: Objektorientierte Programmierung in Delphi

Spezialgebiet – Informatik Manuel PöterOOP in Delphi GRG XV Auf der Schmelz

17

type TFirstClass = class; // Forward-Deklaration von TFirstClass TSecondClass = class; // Forward-Deklaration von TSecondClass

TFirstClass = class(TObject) public Second: TSecondClass; end;

TSecondClass = class(TObject) public First: TFirstClas ; end;

Eine Forward-Deklaration funktioniert immer nach dem Schema:

Klassenname = class;

Das abschließende Semikolon gibt dem Compiler zu erkennen, dass es sich um eine Forward-Deklaration handelt und die eigentliche Deklaration der Klasse erst später kommt.

1122 AAbbssttrraakkttee KKllaasssseennAbstrakte Klassen sind Klassen, die gewisse Methoden zwar deklarieren, aber nichtimplementieren. Solche Methoden nennt man „abstrakte Methoden“. Unter Delphi deklariertman eine solche Methode durch das Wort abstract das der Funktions- /Prozedur-Deklarationnachgestellt wird. z.B.:

procedure Irgendwas; virtual; abstract;

Natürlich müssen abstrakte Methoden immer als virtual oder dynamic deklariert werden,damit sie von abgeleiteten Klassen überschrieben und implementiert werden können.

Eine abstrakte Methode kann nur in einer Klasse (bzw. Instanz einer Klasse) aufgerufenwerden, in der sie überschrieben wurde.

Der Sinn solcher Klassen ist Anfangs nicht ganz klar ersichtlich, aber mit einem kleinen Bsp.versteht man die Idee die dahinter steckt. Ein gutes Beispiel stellt die abstrakte KlasseTStream dar:

{ TStream abstract class }type TStream = class(TObject) private function GetPosition: Longint; procedure SetPosition(Pos: Longint); function GetSize: Longint; protected procedure SetSize(NewSize: Longint); virtual; public function Read(var Buffer; Count: Longint): Longint; virtual; abstract; function Write(const Buffer; Count: Longint): Longint; virtual; abstract; function Seek(Offset: Longint; Origin: Word): Longint; virtual; abstract; procedure ReadBuffer(var Buffer; Count: Longint); procedure WriteBuffer(const Buffer; Count: Longint);

Page 21: Objektorientierte Programmierung in Delphi

Spezialgebiet – Informatik Manuel PöterOOP in Delphi GRG XV Auf der Schmelz

18

function CopyFrom(Source: TStream; Count: Longint): Longint; function ReadComponent(Instance: TComponent): TComponent; function ReadComponentRes(Instance: TComponent): TComponent; procedure WriteComponent(Instance: TComponent); procedure WriteComponentRes(const ResName: String; Instance: TComponent); procedure WriteDescendent(Instance, Ancestor: TComponent); procedure WriteDescendentRes(const ResName: String; Instance, Ancestor:TComponent); procedure WriteResourceHeader(const ResName: string; out FixupInfo: Integer); procedure FixupResourceHeader(FixupInfo: Integer); procedure ReadResHeader; property Position: Longint read GetPosition write SetPosition; property Size: Longint read GetSize write SetSize; end;

Es gibt mehrere verschiedene Stream-Arten:

- TFileStream (für Dateien)

- TStringStream (zur Bearbeitung von Strings im Speicher)

- TMemoryStream (für den Speicherpuffer)

- TBlobStream (für BLOB-Felder)

- TWinSocketStream (zum Lesen und Schreiben über eine Socket-Verbindung)

- TOleStream (zum Lesen und Schreiben über eine COM-Schnittstelle)

Alle diese Stream-Arten sind von der abstrakten Klasse TStream abgeleitet. Jede dieserStreams implementiert die in TStream nicht implementierten, also abstrakten, Methoden.Damit haben alle Streams dieselben Methoden mit gleichen Namen und gleichen Parametern.Dadurch ergibt sich eine gewisse Kompatibilität. Zum Beispiel implementiert TStream dieMethode CopyFrom(). Diese Methode verlangt als Parameter ebenfalls ein TStream-Objektund verwendet intern nur die abstrakten Methoden wie Read und Write. Da schließlich alleNachkommen von TStream diese abstrakten Methoden implementieren, ist z.B. ein Aufrufnach folgendem Schema möglich:

TFileStream.CopyFrom(TMemoryStream, 0);

Statt den Klassennamen müssen natürlich Objekte eingesetzt werden, aber dieses Beispiel sollschließlich nur das Zusammenspiel zweier Klassen veranschaulichen, die beide dieselbeabstrakte Basisklasse haben.

Das Beispiel könnte genauso gut umgekehrt lauten:

TMemoryStream.CopyFrom(TFileStream, 0);

Oder aber auch ganz anders: TWinSocketStream.CopyFrom(TStringStream, 0);

Es würde in jedem Fall zum gewünschten Ergebnis führen.

Damit sollte auch der Sinn von abstrakten Klassen geklärt sein. Abstrakte Klassen sind dazuda, um gewisse Protokolle zu erzwingen und damit Kompatibilitäten zu schaffen.

Page 22: Objektorientierte Programmierung in Delphi

Spezialgebiet – Informatik Manuel PöterOOP in Delphi GRG XV Auf der Schmelz

19

1133 WWeeiitteerree wwiicchhttiiggee SScchhllüüsssseellwwöörrtteerr

1133..11 OOppeerraattoorr –– iissDer Operator is führt eine dynamische Typprüfung durch. Mit ihm kann man den aktuellenLaufzeittyp eines Objekts ermitteln. Der Ausdruck

Objekt is Klasse

gibt True zurück, wenn Objekt eine Instanz der angegebenen Klasse oder eines ihrerNachkommen ist. Trifft dies nicht zu, wird False zurückgegeben (hat Objekt den Wert nil, istder Rückgabewert ebenfalls False). Wenn der deklarierte Typ von Objekt nicht mit Klasseverwandt ist (wenn die Typen also unterschiedlich und nicht voneinander abgeleitet sind), gibtder Compiler eine Fehlermeldung aus. Ein Beispiel:

if ActiveControl is TEdit then TEdit(ActiveControl).SelectAll;

Diese Anweisung prüft zuerst, ob die Variable eine Instanz von TEdit oder einem ihrerNachkommen ist, und führt anschließend eine Typumwandlung in TEdit durch.

1133..22 OOppeerraattoorr –– aass

Der Operator as führt eine Typumwandlung mit Laufzeitprüfung durch. Der AusdruckObjekt as Klasse

gibt eine Referenz auf dasselbe Objekt wie Objekt, aber mit dem von Klasse angegebenenTyp zurück. Zur Laufzeit muß Objekt eine Instanz von Klasse oder einem ihrer Nachkommenbzw. nil sein. Andernfalls wird eine Exception ausgelöst. Wenn der deklarierte Typ vonObjekt nicht mit Klasse verwandt ist (wenn die Typen also unterschiedlich und nichtvoneinander abgeleitet sind), gibt der Compiler eine Fehlermeldung aus. Ein Beispiel:

with Sender as TButton dobegin Caption := '&Ok'; OnClick := OkClick;end;

Die Regeln der Auswertungsreihenfolge machen es häufig erforderlich, as-Typumwandlungenin Klammern zu setzen:

(Sender as TButton).Caption := '&Ok';

1133..33 PPaarraammeetteerr –– SSeellffDer Bezeichner Self verweist in der Implementierung einer Methode auf das Objekt, in demdie Methode aufgerufen wird. Es ist also ein Pointer „auf sich selbst“ und entspricht dem„this” aus C/C++.

Self ist in vielen Situationen hilfreich. Wenn beispielsweise ein Element in einer Methodeseiner Klasse erneut deklariert wird, kann mit Self.Bezeichner auf das Originalelementzugegriffen werden.