Wiederverwendung von Software und Vererbung
Können Sie sich vorstellen, daß eine Firma wie VW oder BMW die Schrauben für ein Auto selbst herstellt? Wohl kaum. Autos werden wie alle aufwendigen Wirtschaftsgüter nur zu einem Teil von der Herstellerfirma selbst gebaut. Viele Einzelteile, wie Reifen, Radios, Klimaanlagen, Sitze usw. werden von anderen Herstellern zugeliefert. Den Grad der Verwendung fertiger Teilprodukte in einem Endprodukt misst man mit Hilfe der Fertigungstiefe. Bei einer Fertigungstiefe von 100% stellt eine Firma alles selbst her, bei 50% wird die Hälfte dazugekauft.
In vielen Seminaren erhielt ich von den Teilnehmern auf die Frage, wie hoch ihre Fertigungstiefe in der Softwareerstellung ist, die Antwort: 100%. Und als Begründung: wir machen alles selbst, wir können es uns nicht leisten, von anderen abhängig zu sein, wir sind so speziell....
Die Entwicklung der Softwareproduktion wird einen ähnlichen Weg nehmen, wie die Entwicklung der mechanischen Produktion. Dazu ist aber die Standardisierung und die Wiederverwendbarkeit von Software unabdingbare Voraussetzung.
Möglichkeiten der Wiederverwendung von Software
Wir können die mögliche Wiederverwendung aus unterschiedlichen Sichtwinkeln betrachten. Eine Betrachtung kommt aus der Modularisierung und der Art, wie man Software übersetzt, bindet und (eventuell) absolut positioniert (für ROM's). Jeder Aufruf eines externen Unterprogramms, jeder Zugriff auf eine globale Variable ist hier bereits Wiederverwendung. Dies ist die mehr technische Seite der Wiederverwendung. C++ bietet hier keine Neuigkeiten, wenn man von den internen Namen absieht.
Eine andere Sichtweise kümmert sich um den logischen Aufbau komplexer Datenstrukturen oder Modelle der Wirklichkeit. In diesem Buch haben wir als Beispiele dafür Kunden, Artikel, Bruchzahlen oder Zeilen kennen gelernt. Solche Modelle (also in C++ die Klassen) können wir erneut als Bausteine betrachten, mit denen größere Klassen aufgebaut werden können.
|
Wiederverwendung von Software und Vererbung Seite 149 |
Beim Aufbau neuer Klassen gibt es zwei unterschiedliche Strategien:
Beide Fälle konnten schon bisher mit Hilfe der Strukturen aufgebaut werden. C++ bietet aber geschützte Klassen. Daher stellt C++ für beide Fälle unterstützende Sprachelemente bereit.
In der folgenden Diskussion wollen wir die beiden Fällen mit Klassen untersuchen.
Objekte als Elemente einer Klasse
In einer neuen Klasse kann man Elemente mit Hilfe einer beliebigen Datentyps deklarieren. Bisher haben wir zumeist Grunddatentypen wie int oder char verwendet. Genauso gut können aber auch Klassen benutzt werden. In den Methoden der neuen Klasse kann man man dann die Methoden der Klasse für das Objekt aufrufen. Definieren wir zuerst eine einfache Klasse Mensch, mit der wir dann weitere Klassen aufbauen wollen.
Unsere Klasse Mensch bildet nur einen sehr kleinen Ausschnitt des echten Menschen nach. Aber für Experimente zur Wiederverwendung von Software soll es genügen. Eine Klasse, die man möglichst so allgemein schreibt, daß sie leicht innerhalb anderer Klasse benutzt und ergänzt werden kann, nennt man Basisklasse.
Der Mensch besitzt einen Vornamen, einen Nachnamen und einen Geburtstag. Die Zahlen für Tag, Monat und Jahr werden dabei in einem Feld gehalten.
|
Seite 150 Einsteigerseminar C++ |
Als Methoden werden ein Konstruktor und zwei Routinen deklariert, die den Namen oder den Geburtstag in einem String liefern.
Im Listing sehen Sie den Konstruktor und die beiden Methoden.
|
Wiederverwendung von Software und Vererbung Seite 151 |
Bei der Ausgabe des Geburtstages müssen die Zahlenangaben im Datum mit "sprintf()" in einen String umgewandelt werden. Die Klasse Mensch verwenden wir nun in einer größeren Klasse.
Als neue, erweiterte Klasse wird eine Person definiert. Der Klassenname ist PersonE für Person mit eingeschlossenem Objekt. Ein Element ist der Mensch mit der grundsätzlichen Beschreibung. Als Zusatzeigenschaft kommt noch (mindestens) das Geschlecht hinzu. Der Zugriff auf das eingeschlossene Objekt geschieht wie üblich mit einer Methode, die an das Objekt gebunden wird.
In der Klassendefinition für PersonE sind die Methoden mit Ausnahme des Konstruktors als inline-Makros geschrieben. Bei der Kürze der Methoden erscheint dies als sinnvoll. Den Konstruktor schreiben wir wie sonst auch als Methode in einer Implementierungsdatei. Innerhalb der Definition der neuen Klasse PersonE wird das Objekt me nur deklariert. Daher erfolgt hier keine Initialisierung mit einem Konstruktoraufruf.
|
Seite 152 Einsteigerseminar C++ |
Wenn ein Objekt angelegt wird, dann muß bei der Initialisierung (also beim Konstruktoraufruf) sowohl ein großes und ein darin enthaltenes, kleineres Objekt initialisiert werden. Dies können wir bei der Definition des neuen Konstruktors angeben.
Für das Elementobjekt existiert bereits ein Konstruktor. Dem neuen Konstruktor verbleibt daher nur die Aufgabe, die neuen Eigenschaften zu initialisieren. In unserem Fall ist das die Eigenschaft Geschlecht.
Dem Funktionskopf des Konstruktors der neuen Klasse folgt die Angabe, wie der Konstruktor für das enthaltene Objekt "" aufzurufen ist. Daher findet sich in der Schnittstellendefinition des neuen Konstruktors die Definition der formalen Parameter, die danach im Konstruktoraufruf für me verwendet werden können. Im Konstruktoraufruf für me können beliebige Variable oder Konstante stehen. Der häufigste Fall ist es jedoch, daß, wie im obigen Beispiel, Parameter des übergeordneten Konstruktors benutzt werden.
|
Wiederverwendung von Software und Vererbung Seite 153 |
Die Reihenfolge der Konstruktoraufrufe ist: zuerst für das Element me und danach der Gesamtkonstruktor.
Die Arbeitsweise unserer bisherigen Klassen können wir mit einem kleinen Hauptprogramm erkunden (im Bild 12-5).
Neben der üblichen Headerdatei stdio.h (für konventionelle Ein- und Ausgaben) holen wir noch persone.hpp. In der Zeile 7 wird ein Puffer angelegt, der für die Rückgaben des Namens und des Geburtstages benötigt wird. In der folgenden Zeile wird ein Objekt Ich der Klasse PersonE angelegt. Dabei müssen zwei Konstruktoren ablaufen: ein Konstruktor für das in Ich enthaltene me und der Konstruktor für die weiteren Eigenschaften von Ich.
Konstruktoren für globale Variable laufen beim Programmstart ab, bevor die erste Vereinbarung oder Anweisung in "main()" ausgeführt wird. Zuerst läuft der Konstruktor der Elementklasse, dann der Konstruktor der neuen Klasse.
|
Seite 154 Einsteigerseminar C++ |
Rahmen10
Die Ausgabe sieht wie folgt aus. (Zeilennummern sind eingefügt)
Mit Objekten als Elemente von Klassen kann man neue Klassen aufbauen und innerhalb der Methoden auf das Elementobjekt zugreifen. Die Zugriffsrechte unterscheiden sich aber nicht von der allgemeinen Verwendung. Wir dürfen nur auf die public-Elemente zugreifen.
Diese Art der Wiederverwendung ist in vielen Fällen angebracht. Insbesondere in den Fällen, in denen zwischen den einzelnen Elementen einer neuen Klasse keine unmittelbaren logischen Beziehungen bestehen. Im Bild 7 setzt sich eine Benutzerschnittstelle aus dem Eingabe- und dem Ausgabeobjekt zusammen.
|
Wiederverwendung von Software und Vererbung Seite 155 |
Beide Teilobjekte könnten auch unabhängig voneinander existieren. Die Klasse Benutzer_Schnittstelle bildet sozusagen nur eine Klammer. Die umschließende Klasse muß ihre Zugangsfunktionen mit Hilfe von Methodenaufrufen an die Teilobjekte bereitstellen. Wir haben das auch im Bild 12-3 getan.
Wiederverwendung durch Vererbung
Der zweite Fall der Wiederverwendung von Software war die Spezialisierung. Eine neue Klasse sollte so etwas wie die bestehende Klasse sein, nur spezieller. Wenn eine allgemeine Klasse Fahrzeug existiert, dann kann man damit eine Klasse PKW oder LKW oder Bus definieren. Die neue Klasse wird als Erweiterung der bestehenden empfunden. Es ist sicher einleuchtend, daß man mit einem Fahrzeug anfahren und bremsen kann.
|
Seite 156 Einsteigerseminar C++ |
Die Idee der Vererbung in objektorientierten Sprachen ist es nun, die Methoden der Basisklasse (hier: Fahrzeug) automatisch auch für die neue Klasse zur Verfügung zu stellen. Wenn ein Objekt der Klasse Fahrzeug anfahren kann, dann soll auch ein PKW automatisch anfahren können.
Die Vererbung ist eine Dienstleistung der Sprache, die die automatische Wiederverwendung von Elementen der Basisklasse in abgeleiteten Klassen ermöglicht.
Um die grundlegenden Möglichkeiten der Vererbung kennen zulernen, wird das gerade benutzte Beispiel leicht modifiziert und mit Vererbung wiederholt. Weiterhin benutzen wir die unveränderte Basisklasse Mensch.
In der abgeleiteten Klasse PersonV (für Person mit Vererbung) teilen wir dem Compiler in der Zeile 4 nach dem neuen Klassennamen PersonV mit, daß er die Klasse Mensch als Basisklasse mit einschließen soll. Das Schlüsselwort public bestimmt die Art der Vererbung. (siehe: offene und geschlossene Vererbung)
|
Wiederverwendung von Software und Vererbung Seite 157 |
Wenn die neue Klasse die Basisklasse erbt, dann können wir die Methoden der Basisklasse automatisch wiederverwenden. Die Methode für die Ausgabe des Geburtstages wird in der abgeleiteten Klasse nicht mehr benötigt. Nur zur Demonstration wurde in der Zeile 13 (im Bild 12-8) eine Methode ZeigNamen definiert. Sie ruft die Methode GibNamen() der Basisklasse auf. Beachten Sie, daß die Methode der Basisklasse automatisch an das geerbte Basisobjekt gebunden wird. Daher kann man eine zugängliche Basismethode ohne weitere Angabe aufrufen. Im vorhergegangenen Beispiel mußten wir den Methodenaufruf explizit an das Elementobjekt binden. Oder, um es technischer zu beschreiben, der Compiler passt den verdeckten Parameter this automatisch an.
In der Zeile 8 der Implementierung (Bild 12-9) finden wir an Stelle des Objektnamens den Klassennamen. Der Compiler wird dabei informiert, das Objekt, das er automatisch hinzugefügt hat, mit Hilfe des Konstruktors initialisieren soll.
Unser Hauptprogramm braucht nicht geändert werden, wenn man von der neuen Informationsdatei einmal absieht. Und auch die mit dem Hauptprogramm erzeugte Ausgabe ist die gleiche wie im letzten Beispiel.
|
Seite 158 Einsteigerseminar C++ |
Geändert hat sich nur eines: der Compiler stellt für das abgeleitete Objekt automatisch die Methoden des Basisobjektes zur Verfügung.
Die Vererbung stellt also einen Satz von Spielregeln dar, die bestimmen, wie eine vorhandene Klasse automatisch beim Aufbau einer neuen Klasse verwendet werden kann.
Die Ausgabe wird wieder wie vorher.
|
Wiederverwendung von Software und Vererbung Seite 159 |
Die möglichen Variationen der Vererbung werden wir nun im einzelnen erkunden.
Erweiterung des Schutzkonzeptes in Klassen
Bisher kennen wir innerhalb einer Klasse zwei Bereiche mit unterschiedlichen Schutzstufen:
Der private Bereich (private) beinhaltet alle Elemente, also Eigenschaften und Methoden, die nur innerhalb der Klasse sichtbar sein sollten. Nur die Methoden oder Freundfunktionen, die in der Klasse deklariert worden sind, dürfen auf den privaten Bereich zugreifen. Die Deklaration innerhalb der Klasse erteilte sozusagen eine Zugriffslizenz.
Der öffentliche (public) Bereich erlaubte den Zugriff von beliebigen anderen Programmteilen. Er bildet die Schnittstelle einer Klasse nach außen. Diese grobe Unterteilung stört gelegentlich. Gerade im Rahmen der Vererbung will man innerhalb der abgeleiteten Klasse häufig auf Elemente der Basisklasse zugreifen. C++ führt daher eine dritte Schutzstufe ein: protected.
Alle Elemente innerhalb der Basisklasse, die im protected-Abschnitt stehen, dürfen von den Methoden der abgeleiteten Klasse genauso benutzt werden, als stünden sie im public-Bereich. Der Zugriff ist damit innerhalb der Methoden (und Freunde) abgeleiteter Klassen jederzeit möglich. Aber alle anderen Programmteile bleiben nach wie vor ausgeschlossen.
Die Klasse kennt insgesamt drei unterschiedliche Schutzstufen:
Eine vollständige Klassendefinition sieht nunmehr wie folgt aus:
|
Seite 160 Einsteigerseminar C++ |
Offene und geschlossene Vererbung
Bei der Vererbung einer Basisklasse an eine abgeleitete Klasse können wir festlegen, ob man die Schnittstelle der Basisklasse, also den public-Bereich, auch außerhalb der abgeleiteten Klasse noch sehen soll (offene Vererbung).
Eine Basisklasse kann private eingebunden werden. Damit legen wir fest, daß die zugänglichen Teile der Basisklasse, der protected und der public-Bereich so behandelt werden, als stünden sie im private-Bereich der abgeleiteten Klasse (geschlossene Vererbung).
Die abgeleitete Klasse verwendet die Basisklasse also nur intern. Eine derartige Verwendung ist z. B. bei Treibern oder Protokollhierarchien sinnvoll. Man hat für die Kommunikation zwischen Rechnern ein ISO-7-Schichten-Modell definiert. Jede Schicht erfüllt genau eine Aufgabe und verlässt sich ausschließlich auf die darunter liegende Schicht. Die jeweils obere Schicht verdeckt die darunter liegende vollständig. Hier würde die geschlossene Vererbung sinnvoll angewendet.
|
Wiederverwendung von Software und Vererbung Seite 161 |
Im Gegensatz dazu kann man eine Basisklasse public einbinden. Damit erlauben
wir, daß auch auf ein Objekt der abgeleiteten Klasse public-Methoden
der Basisklasse angewendet werden können. Der protected-Bereich der Basisklasse
wird wie der eigene "protected"-Bereich behandelt. Weitere Ableitungen
können also auch darauf zugreifen. Diese Art der Vererbung nennen wir offene
Vererbung.
Die offene Vererbung ist immer dann sinnvoll einzusetzen, wenn eine abgeleitete Klasse eine Spezialisierung der Basisklasse bietet. Die abgeleitete Klasse ist also so etwas wie die Basisklasse, nur spezieller.
Methoden der Basisklasse können hier bei Bedarf auch durch gleichnamige Methoden in der abgeleiteten Klasse überlagert werden.
Bei der Vererbung können die Schutzrechte der Basisklasse übernommen oder eingeschränkt werden. In keinem Fall ist durch eine Vererbung die Erweiterung der Schutzrechte möglich.
|
Seite 162 Einsteigerseminar C++ |
Betrachten wir zusammenfassend die drei Schutzbereiche einer Klasse zusammen
mit den beiden Möglichkeiten der Vererbung, so können wir die Zuordnung
wie folgt darstellen.
|
Wiederverwendung von Software und Vererbung Seite 163 |
Einfache und mehrfache Vererbung
Bisher haben wir beim Aufbau einer abgeleiteten Klasse genau eine Basisklasse verwendet. Diese Vorgehen entspricht vielen technischen Problemen.
Baut man eine Klassenhierarchie mit der einfachen Vererbung auf, so ergibt sich ein umgekehrter Baum. Diese Struktur kennen wir von jedem hierarchischen Dateisystem wie es bei Linux oder Windows üblich ist.
Im Standard-C++ ist auch eine Mehrfachvererbung möglich. Bei der Definition einer abgeleiteten Klasse können mehrere Basisklassen benutzt werden. So schön und allgemein dies klingt, kann es doch zu Problemen führen. Angenommen, wir leiten mit einfacher Vererbung von einer Basisklasse zwei unterschiedliche neue Klassen ab (im Bild 12-17 C1 und C2). Diese beiden Klassen benutzen wir dann, um mit mehrfacher Vererbung eine Zielklasse zu definieren. In diesem Fall hätten wir zwei Objekte der Basisklasse in einem Objekt der Klasse D. Und das führt zu Problemen.
|
Seite 164 Einsteigerseminar C++ |
Betrachten wir im folgenden zuerst die notwendigen Sprachelemente für die Mehrfachvererbung und danach die Lösungsmöglichkeit für das Problem des mehrfachen Erbens der gleichen Basisklasse.
Klassendefinition mit mehrfacher Vererbung
Bei der Definition der abgeleiteten Klasse kann man nach dem Klassennamen und einem Doppelpunkt eine Liste von Basisklassen angeben. Für jede eingeschlossene Klasse wird dabei einzeln die Art der Vererbung definiert.
Das Bild 12-18 enthält einen Auszug aus der Informationsdatei iostream.h, die so oder so ähnlich bei einer üblichen Ein-/Ausgabebibliothek vorhanden ist.
Die Klasse iostream verwendet als Basisklasse eine Ausgabeklasse ostream und für die Eingabe die Klasse istream. Zusammen erlauben sie mit einem Objekt der Klasse iostream sowohl einzulesen als auch auszugeben. Hier werden die Basisklassen beide mit offener Vererbung eingeschlossen (public).
|
Wiederverwendung von Software und Vererbung Seite 165 |
Rahmen36Der Konstruktor wird analog zur einfachen Vererbung geschrieben.
Der Aufruf der Konstruktoren geschieht in der Reihenfolge, wie in der Liste nach dem Konstruktornamen angegeben. Der Konstruktor der abgeleiteten Klasse wird am Schluß aufgerufen.
Der Compiler verhindert auf Wunsch das mehrfache Einschließen von Basisklassen. Dazu benutzt man das Schlüsselwort virtual. Dieses Schlüsselwort werden wir im Kapitel über Polymorphie noch mit einer anderen Bedeutung kennen lernen.
|
Seite 166 Einsteigerseminar C++ |
Beispiel für die Vererbung - ein Fenstersystem für den PC
Ein erlebbares Beispiel für die Vererbung können wir am DOS-PC und seinem Bildschirmspeicher nachvollziehen. Wir betrachten dabei den Bildschirm als Feld von einzelnen char-Variablen, auf die immer ein Attribut-Zeichen folgt.Die gesamte Größe des Bildspeichers ist bei 25 Zeilen, 80 Zeichen pro Zeile und 2 char pro Bildschirmzeichen 4000 Bytes. Das Beispiel ist einfach - aber leider nur für DOS-PC Nutzer.
|
Wiederverwendung von Software und Vererbung Seite 167 |
Die Basisklasse video definiert den Zugriff auf den Videospeicher. Sie stellt mit ihren Methoden den Zugang bereit. Aus Geschwindigkeitsgründen schreiben alle Methoden direkt in den Bildspeicher.
|
Seite 168 Einsteigerseminar C++ |
Da wir nur eine sehr einfache Betriebsart, nämlich den Textmodus mit 25 Zeilen und 80 Spalten benötigen, ist die Kompatibilität mit allen bekannten VideoKarten gegeben.
Nachdem der physikalische Zugriff auf den Bildspeicher definiert wurde, kann die Klasse video als Basisklasse in einem einfachen Fenstersystem verwendet werden. Eine Fenstermethode kann wegen der Vererbung auf die Methoden der Basisklasse zugreifen und das Fenster verwalten. Wir müssen bei der Konstruktion der Klassen noch berücksichtigen, daß es nur einen Bildschirm mit seinem Bildspeicher, aber darauf mehrere Fenster geben kann.
Die Klasse video enthält nur die notwendigsten Elemente, um das Beispiel nicht zu komplex werden zu lassen. Denkbare Erweiterungen wären z. B. Eigenschaften für Zeile und Spalte, um auch die erweitereten Betriebsarten der VGA-Karten benutzen zu können.
Als private Eigenschaften benutzen wir ausschließlich Klasseneigenschaften. Da diese nur einmal angelegt werden passt das gut zum Bildschirm, den es auch nur einmal gibt. Die erste Eigenschaft ist ein Merker. Die Grundinitialisierung durchlaufen wir nur, wenn der Merker noch nicht gesetzt ist.
In der basis wird das im aktuellen Videoadapter benutzte Speichersegment abgelegt. Zwei Werte gibt es. Die S/W-Hercules-Karte und die alte MGA-Karte benutzen 0xb000 und die anderen 0xb800.
Im Attribut "attr" wird die Voder- und Hintergrunddarstellung festgelegt. Die entsprechenden Bitkombinationen sind bei den DOS- Systemaufrufen beschrieben.
Die letzte Eigenschaft ist ein Zeiger auf den Bildschirmspeicher. Er wurde als "far"-Zeiger deklariert. Damit ist dieser Zeiger unabhängig vom verwendeten Speichermodell 32 Bit groß und kann Segment und Offset der Adresse aufnehmen.
Die Methoden setzen sich aus dem Konstruktor, Schreib- und Lesemethoden sowie einigen Methoden zur Unterstützung der Fensterklasse zusammen. Ein Methodenpaar kann eine box, also eine rechteckigen Bildschirmausschnitt, in einen Puffer schreiben oder ihn aus dem Puffer wiederherstellen.
|
Wiederverwendung von Software und Vererbung Seite 169 |
Die beiden letzten Methoden dienen dem Auf- und Abrollen (scrollen) des Bildschirms.
|
Seite 170 Einsteigerseminar C++ |
Rahmen47
Die Basisklasse video werden wir dann im Fenstersystem wiederverwenden. Aber zuerst zur Implementierung. In der Methodendatei werden zuerst die Klasseneigenschaften definiert und weitgehend initialisiert.
|
Wiederverwendung von Software und Vererbung Seite 171 |
In der Implementierung benötigen wir die Informationsdatei dos.h. Sie definiert eine Nachbildung der Prozessorregister, die "REGS" heißt. Damit kann man BIOS oder DOS-Aufrufe machen. Dies geschieht mit der Bibliotheksfunktion int86" in den Scrollmethoden und im Konstruktor.
|
Seite 172 Einsteigerseminar C++ |
|
Wiederverwendung von Software und Vererbung Seite 173 |
Das Makro MK_FP liefert aus Segment und Offset eine 32-Bit-Adresse. Wir laden damit den Zeiger auf den Bildspeicher. Die Methoden v_za_schreiben und v_s_schreiben sind die Ausgaberoutinen. v_za_schreiben überträgt ein Zeichen mit Attribut und v_s_schreiben kopiert einen Text auf den Bildschirm. Der Text wird durch eine \0" abgeschlossen. Dieser Wert hat auch die Funktion des Booleschen falsch". Die while-Schleife wird daher am Textende abbrechen. Der Zeiger überspringt beim Schreiben die Attribute.
Die Lesemethode v_za_lesen benutzt eine Vereinigung, um eine Typwandlung von zwei char auf eine int- Variable durchzuführen. Zurück gegeben wird dann ein Zeichen zusammen mit seinem Attribut.
Der nicht sichtbare Schreibzeiger (cursor) wird über den aktuellen Zeigerstand realisiert. v_set_cursor berechnet den Zeiger neu.
Die Methode v_loeschen setzt einen Hilfszeiger auf den Anfang des Bildspeichers und schreibt an alle 2000 Positionen ein Leerzeichen zusammen mit dem derzeit gültigen Attribut. Da der Bildspeicher zusammenhängt, brauchen wir auf eine Zeileneinteilung keine Rücksicht nehmen. Schließlich setzen wir den Cursor auf den Anfang.
Die Methoden zur Bearbeitung einer Box, also eines rechteckigen Bildschirmausschnittes, bearbeiten Zeile für Zeile den Ausschnitt. Das Rollen (scrollen) des Bidschirms verläßt sich auf geeignete BIOS-Aufrufe. Dabei wird der Video-Interrupt 0x10 verwendet.
Und schließlich stellt der Konstruktor die Art des benutzten Videoadapters fest. Wenn der BIOS-Aufruf den Videomodus 7" meldet, dann ist entweder eine Hercules-Karte oder ein älterer MGA im Gerät. In allen anderen Fällen verwenden wir das Videosegment 0xb800. Wichtig für den Konstruktor ist der Merker v_ini". Nach dem ersten Durchlauf wird er auf 1" gesetzt und die Initialisierung später nicht mehr wiederholt. Dies ist notwendig, da bei jedem neuen Fenster die Initialisierung der Basisklasse erneut aufgerufen wird.
Schauen wir uns nun die Klasse Fenster an.
|
Seite 174 Einsteigerseminar C++ |
Auf den folgenden Seiten finden Sie das Listing der Methoden. Danach werden wir Einzelheiten diskutieren.
|
Wiederverwendung von Software und Vererbung Seite 175 |
Rahmen53
|
Seite 176 Einsteigerseminar C++ |
.
Rahmen54
|
Wiederverwendung von Software und Vererbung Seite 177 |
Fortsetzung nächste Seite
Rahmen55
|
Seite 178 Einsteigerseminar C++ |
Fortsetzung nächste Seite.
Rahmen56
|
Wiederverwendung von Software und Vererbung Seite 179 |
Fortsetzung folgt.
Rahmen57In der Klasse Fenster haben wir eine Klasse video geerbt. damit können wir auf alle öffentlich zugänglichen Elemente der Basisklasse automatisch zugreifen. Ein Fenster wird in unserem Fall durch seine Eckpunkte am Bildschirm, Vorder- und Hintergrunddarstellung im Attribut, die Cursorposition im Fenster, die Angaben für den Rettbereich für überdeckte Bytes sowie die Angabe, mit welchem Zeichen ein leeres Fenster gefüllt werden soll, beschrieben.
Neben dem Konstruktor sind Methoden zum Schreiben von Texten und Zeichen, zum Lesen eines Zeichens mit seinem Attribut, zur Ausgabe eines Titels und zum Rollen des Fensterinhaltes vorhanden.
In diesem etwas umfangreicheren Beispiel, können wir sehr schön die leichte Verwendung der Methoden der Basisklasse sehen. (z. B. in der Zeile 20, 30...)
Für Sie, lieber Leser, könnte es eine spannende Aufgabe sein, das Fenster um einige neue Methoden zu erweitern. Dazu sollten die Verbesserung des Rollens gehören (momentan wird beim Rollen die neue Zeile noch nicht mit dem Standardwert in w_leer initialisiert) und eine Möglichkeit, den Cursor im Fenster zu positionieren. Dabei würden Sie sehen, wie leicht und sicher die Klasse zu erweitern ist.
|
Seite 180 Einsteigerseminar C++ |
|
Wiederverwendung von Software und Vererbung Seite 181 |
Das Hauptprogramm zum Fenstersystem finden sie im Bild 12-26.
Das Fenstersystem könnte noch erheblich verbessert werden. Eine der Voraussetzung für das richtige Funktionieren ist hier, daß überlappende Fenster in der umgekehrten Reihenfolge gelöscht werden, wie sie angelegt wurden. Rahmen und Fenster könnten unterschiedliche Attribute wie das restliche Fenster benutzen. Ein Menüsystem in der ersten Zeile könnte genauso ergänzt werden, wie die Eingabefunktionen für Maus und Tastatur.
Das Beispiel kann mehrere Dinge zeigen. Die Basisklasse kapselt alle Hardware-abhängigen Grundfunktionen und die abgeleitete Klasse kann dann ohne jede Hardware-Kenntnis ein Fenstersystem aufbauen. Würde man eine andere Hardware benutzen müßte nur eine Klasse neu geschrieben werden. Elegant - nicht wahr?
Kopierkonstruktoren für abgeleitete Klassen
Einen Spezialfall konnte das Beispiel nicht zeigen: den Kopierkonstruktor. Der Kopierkonstruktor in einer abgeleiteten Klasse hat auch die Aufgabe, das geerbte Basisobjekt mit zu kopieren. Dazu benötigt er den Kopierkonstruktor der Basisklasse.
Der Kopierkonstruktor erhält als Parameter ein Objekt der eigenen Klasse per Referenz. Dieses Objekt wird, ebenfalls per Referenz, an den Konstruktor der Basisklasse weitergegeben. Die notwendige Typwandlung wird der Compiler durchführen. Referenzen und Zeiger auf Basisklassen dürfen in C++ auch mit Referenzen bzw. Zeigern auf abgeleitete Klassen vorbelegt werden. Man sagt, daß Referenzen und Zeiger auf Basisklassen kompatibel sind, zu den von ihnen abgeleiteten Klassen.
|
Seite 182 Einsteigerseminar C++ |
Bemerkungen zum Beispiel
Das Beispiel ist deutlich größer geraten, als die anderen Beispiele im Buch. Es sollte Ihnen Mut machen, auch auf den ersten Blick komplexe und unübersichtliche Probleme anzugehen. Dank der Klassen ergibt sich eine logische Untergliederung des Gesamtproblems. Und beim Schreiben einer Klasse konzentriert man sich nur jeweils auf den speziellen Aspekt der Klasse. Und mit der Vererbung oder der Nutzung als Element kann man dann auf den vorhandenen Klassen sicher aufbauen.
Vielleicht suchen Sie sich auch ein größeres Problem, das Sie interessiert, und fangen an, das große Problem in seine Bestandteile, seine Klassen zu zerlegen.
Hinweise zur Weiterarbeit
1) Schreiben Sie eine Methode, die den Cursor innerhalb eines Fensters positionieren kann. Die Koordinaten sollen sich dabei auf die linke obere Fensterecke innerhalb des Fensterrahmens beziehen.
2) Ergänzen Sie die Methoden für Rollen um die richtige Vorbelegung der nachgezogenen Zeile mit dem Wert in w_leer.
3) Eine größere Aufgabe wäre es, ein Menüsystem mit den Methoden der Basisklasse video zu schreiben.
|
Wiederverwendung von Software und Vererbung Seite 183 |
Im nächsten Kapitel
Mit Hilfe der Vererbung können wir nun beginnen, das Senden von Botschaften an Objekte zu verallgemeinern. Wie erwähnt, spricht man in der OOP vom Senden von Botschaften an Objekte anstelle vom Aufruf einer Funktion. Wenn wir an verschiedene Objekte die Botschaft Drucke dich! senden, dann müßte das Objekt entsprechend reagieren.
Bisher können wir an vordefinierte Objekte derartige Botschaften senden. Der Compiler kann zur Übersetzungszeit ermitteln, welche Methode (welche Botschaft) zu welchem Objekt gehört. Was aber, wenn wir erst zur Laufzeit wissen, welche Objekte es tatsächlich gibt? So legt der Benutzer mit einem Zeichenprogramm interaktiv graphische Objekte an. Welche das sind, erfährt das Programm erst zur Laufzeit.
Im nächsten Kapitel werden wir Botschaften an Objekte schicken.
|
Seite 184 Einsteigerseminar C++ |