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.


Rahmen1

Im Listing sehen Sie den Konstruktor und die beiden Methoden.


Rahmen3


Wiederverwendung von Software und Vererbung    Seite 151


Rahmen4

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++


Rahmen6

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


Rahmen8

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)


Rahmen12

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


widrverwgem

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”.


Rahmen16

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


Rahmen18

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.


Rahmen20

Die Ausgabe wird wieder wie vorher.


Rahmen22


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++


Rahmen24

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

vererbprgem 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++

vererbpugem 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.


PROTECTGEM


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.


vrbeinfgem

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++


vrbmehrgem

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.


Rahmen38

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.

Virtuelle Basisklasse

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++


Rahmen42

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.


 

bildschgem


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.


Rahmen44


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


Rahmen46

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


Rahmen48

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++


Rahmen49



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 Boole’schen ”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++


Rahmen51

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.

Rahmen57

In 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++


Rahmen59


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.


Rahmen61

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++