Späte Bindung oder Polymorphismus


Mit diesem Kapitel kommen wir zum letzten der grundlegenden Punkte der Objekt Orientierten Programmierung. Nach der Kapselung, den Hilfsmitteln zur natürlichen Programmierung und der Vererbung bleibt der Polymorphismus noch offen. Die wörtliche Übersetzung aus dem Griechischen ist Vielgestaltigkeit, was uns aber auch nicht schlauer macht. Ein anderer, mehr technisch orientierter Ausdruck dafür, ist die späte Bindung.

Bindung Objekt - Methode

Kommen wir an dieser Stelle noch einmal auf den Begriff der Bindung zurück. Die Bindung gibt an, welches Objekt mit welcher Methode bearbeitet werden soll. Wie wir schon gesehen haben, wird intern die Bindung mit Hilfe eines Zeigers “this” realisiert. Dies ist aber nur ein Teil der Bindung. Neben der Adreßermittlung für “this” prüft der Compiler noch den verwendeten Datentyp. Wenn wir ein Programm haben, das für alle verwendeten Klassen eine “print()”-Methode zur Verfügung stellt, dann muß der Compiler auch sicherstellen, daß die richtige Methode ausgewählt und ihr der “this”-Zeiger übergeben wird.

Frühe Bindung

Kann der Compiler während der Übersetzungszeit sowohl die Adresse des Objektes als auch seinen Typ ermitteln, spricht man von früher Bindung.


Im Bild 13-1 sehen Sie die beiden Möglichkeiten für eine frühe Bindung. Entweder kennt der Compiler beim Aufruf einer Methode das Objekt mit Namen. Dann weiß er natürlich die Klasse (den Datentyp), zu dem das Objekt gehört. Weiter kennt er die Adresse des Objektes. Die Adresse kann er nun an den Zeiger “this” der Methode weitergeben. Mit Hilfe des bekannten Datentyps kann er die richtige aus möglichen Methoden auswählen. Wegen der Überlagerung könnten ja beliebig viele unterschiedliche Methoden gleichen Namens existieren. (Zeile 10)


Späte Bindung oder Polymorphismus    Seite 185

Rahmen1Die zweite Möglichkeit ergibt sich, wenn wir mit einem Zeiger arbeiten. Beim Aufruf der Methode "print()" holt der Compiler die Adresse des Objektes aus dem Zeiger und gibt sie an “this” weiter. Natürlich erwartet er, daß der Programmierer den Zeiger korrekt vorbelegt hat. Den Typ, den der Compiler zur Auswahl aus den möglichen Methoden benötigt, nimmt er vom Zeigertyp. In unserem Fall haben wir einen Zeiger auf “ratio”. Daher wählt der Compiler die Methode “print()” der Klasse “ratio” (Zeile 15).


Das gerade benutzte Beispiel wollen wir auf ein Feld von Zeigern (Bild 13-2) erweitern. Felder werden oft zur Verwaltung eingesetzt. Alle in einem Programm verwendeten Objekte einer Klasse könnten in einem Feld verwaltet werden. Dies wird man insbesondere dann tun, wenn die Objekte dynamisch angelegt werden. Momentan soll das Feld nur Objekte einer einzigen Klasse enthalten.


Seite 186    Einsteigerseminar C++

Rahmen3Späte Bindung

Die späte Bindung stellt erst während der Laufzeit fest, welches Objekt mit welcher Methode bearbeitet werden soll. Nehmen wir dazu wieder das Feld und laden es mit Adressen unterschiedlicher Objekte.


Nehmen Sie als Beispiel ein Zeichenprogramm. Der Benutzer legt nacheinander Kreise, Rechtecke oder Polygone an. Das Zeichenprogramm muß alle Objekte dynamisch anlegen und verwaltet die gewählten Objekte in einer Tabelle. Beim Auffrischen des Bildschirms müssen dann nacheinander alle Objekte gezeichnet werden. Dazu brauchen wir die Möglichkeit, mit einem Zeiger auf beliebige Objekte zeigen zu können; und mit diesem Zeiger dann auf Methoden. Dies ist dann späte Bindung.



Späte Bindung oder Polymorphismus    Seite 187

Um die späte Bindung realisieren zu können, benötigen wir zwei Dinge:


In C++ kennen wir die Vererbung. Definitionsgemäß kann ein Zeiger auf eine Basisklasse auch auf Objekte von abgeleiteten Klassen zeigen. Damit haben wir zwar keinen völlig allgemeinen Zeiger, aber immerhin einen Zeiger auf eine Gruppe von Objekten. In unserem Feldbeispiel heißt das, daß das Feld Adressen von beliebigen, voneinander abgeleiteten Objekten aufnehmen kann. Ein wirklich allgemeiner Zeiger wäre typlos. Dies ist bei Zugriffen auf Objekte nicht gestattet. “void *” kann daher als Typ des Zeigers nicht verwendet werden.


Um die zweite Forderung erfüllen zu können, müssen wir in einem C++ - Objekt eine Zusatzinformation ablegen, die während der Laufzeit, also mit Programmcode, ausgewertet werden kann. Diese Zusatzinformation beinhaltet den Typ. Sie heißt . Im Normalfall fehlt sie in C++. - Objekten


C++ unterscheidet sich hier grundsätzlich von anderen Objekt Orientierten Sprachen wie Smalltalk. In einem Objekt in C++ gibt es erst einmal keine Metainformationen. Diese Metainformationen sind Informationen über das Objekt. Denkbare Metainformationen wären der Name des Objektes, ein Verweis auf die Klasse und deren Aufbau etc. Die Größe eines Objektes läßt sich in C++ mit dem “sizeof”-Operator ermitteln. Er liefert exakt die Summe der Elemente (sofern beim Übersetzen die Ausrichtung auf Wortgrenzen (alignment) abgeschaltet ist).


Schauen wir uns zuerst ein klassisches Beispiel für die späte Bindung an und kommen anschließend auf die Realisierung zurück.


Das Beispiel soll eine allgemeine, graphische Klasse definieren. Hier ist es ein Punkt am Bildschirm. Von dieser Basisklasse leiten wir eine Linie und ein Quadrat ab. Mit diesen drei Klassen können wir dann in einem Hauptprogramm beliebige Objekte anlegen und deren Adressen in einem Feld verwalten. Und schließlich sollen mit einer Schleife alle Objekte am Bildschirm angezeigt werden.


Seite 188    Einsteigerseminar C++

Rahmen5Die Basisklasse definiert (Bild 13-3) zwei Eigenschaften, die den Ort des Punktes angeben. Um den Methoden der abgeleiteten Klassen den Zugriff zu ermöglichen, wurden sie in den “protected”-Abschnitt der Klasse gelegt.


Die Methode “print()” wurde in der Zeile 16 ausdrücklich für die späte Bindung vorgesehen. Das Schlüsselwort “virtual” teilt dem Compiler die geänderte Behandlung mit. (Auch die Destruktoren müssen virtual sein.)


Die Implementierung (Bild 13-4) entspricht dem gewohnten Bild. Der Konstruktor belegt die Eigenschaften vor und die Methode "print()" gibt mit der Funktion “putchar()” ein Sternchen am Bildschirm aus. Die Positionierung des Cursors übernimmt das Makro “POS”. Das Makro sendet mit der “printf()”-Funktion eine ANSI-Steuersequenz zum Bildschirm. In der Makroexpansion werden dann die richtigen Werte für Zeile und Spalte eingesetzt.


Späte Bindung oder Polymorphismus    Seite 189

Rahmen7Leiten wir nun die erste Klasse “linie” ab. Zur Definition einer Linie benötigen wir zumindest einen Anfangs- und einen Endpunkt.


Seite 190    Einsteigerseminar C++


Rahmen9

Um uns das Leben nicht unnötig zu erschweren, soll die Linie nur waagrecht oder senkrecht laufen. Damit sind entweder die Spalten oder die Zeilen der beiden Endpunkte gleich. Den Startpunkt liefert “grafik”.


Späte Bindung oder Polymorphismus    Seite 191

  

Rahmen11

Die Zeile wird durch eine Reihe von Sternchen markiert, die mit “putchar()” wie bei “grafik ausgegeben werden. In abgeleiteten Klassen finden wir keinen Hinweis auf eine späte Bindung.



Seite 192    Einsteigerseminar C++

Rahmen13Beim Quadrat gehen wir analog zur Linie vor. Wieder wird die Basisklasse “grafik” geerbt. Diesmal gibt “grafik” den linken oberen Punkt an.


Rahmen15

Fortsetzung des Listings nächste Seite.


Der Konstruktor initialisiert die eigenen Eigenschaften, die nun die rechte untere Ecke des Quadrates angeben. Die Koordinaten der linken, oberen Ecke werden an den Konstruktor der Basisklasse weitergegeben.


Späte Bindung oder Polymorphismus    Seite 193

In der "print()"- Methode werden die vier Seiten des Quadrates mit vier “for”- Schleifen realisiert. Man hätte das “quadrat” auch mit vier Objekten der Klasse “linie” realisieren können.


Rahmen16

Mit Hilfe der bisher definierten Klassen kann nun das Hauptprogramm geschrieben werden.


Das Hauptprogramm (Bild 13-9) simuliert den beschriebenen Anwendungsfall “Zeichenprogramm”. Da unser Zeichenprogramm mit Punkten, Linien und Rechtecken umgehen können soll, holt sich das Programm mit “include"- Anweisungen zuerst die Klassendefinitionen. Das Verwaltungsfeld für alle gewünschten Objekte hat den Datentyp ”Zeiger auf Basisklasse", hier also “grafik *”.


Beim Ablauf wird der Bildschirm gelöscht, um eine vernünftige Anzeige zu gewährleisten. Mit “” legen wir einige Objekte an und speichern deren Adressen im Verwaltungsfeld. Dies soll die Benutzereingabe simulieren.


Und schließlich gibt eine Schleife alle gültigen Objekte nacheinander am Bildschirm aus. Dies simuliert den Vorgang “Bildschirm neu aufbauen”.


Seite 194    Einsteigerseminar C++

Rahmen18Die entscheidende Stelle ist der Schleifenkörper. Mit Hilfe eines Zeigers aus dem Verwaltungsfeld rufen wir eine Methode "print()" auf. In einem Zeiger des Feldes können wir die Adressen von verschiedenen Objekten speichern, die zu verschiedenen Klassen gehören. Welches “print()” aus welcher Klasse nehmen wir?


In der Basisklasse haben wir die Methode "print()" mit “virtual” für die späte Bindung vorgemerkt. In den abgeleiteten Klassen wurde die Methode “print()” immer wieder erneut definiert. Da die Basisklassenmethode “virtual” war, merkt sich der Compiler dies für die abgeleiteten Methoden ebenfalls.



Späte Bindung oder Polymorphismus    Seite 195

Mit dem Schlüsselwort “virtual” wird bewirkt, daß der Compiler in alle Objekte, die die Basisklasse benutzen, eine Zusatzinformation ablegt. Sie gibt den Typ des Objektes an. Die Objekte mit später Bindung werden dadurch größer als ohne späte Bindung. Beim Aufruf der virtuellen Methode "print()" im Hauptprogramm holt sich der Programmcode die Typinformation aus dem Objekt, auf das der Zeiger gerade zeigt, und wählt damit die richtige Methode aus.


Damit die späte Bindung funktionieren kann, sind in C++ folgende Voraussetzungen notwendig:



Technisch wird die späte Bindung über Sprungtabellen realisiert. In unserem Beispiel werden intern die Adressen aller "print()"-Methoden in einer virtuellen Methoden-Tabelle (VMT) gespeichert. Beim Zugriff dient die im Objekt abgelegte Typinformation zur Auswahl aus der Tabelle.


Sollte in einer abgeleiteten Klasse keine eigene Definition der virtuellen Methode erfolgen, dann nimmt der Compiler für die Klasse die entsprechende Methode der übergeordneten Klasse. Würde im obigen Beispiel die "print()"- Methode der “linie” fehlen, dann würde der Compiler die Methode der Klasse nehmen, die “linie” geerbt hat, in unserem Fall wäre das schon die Basisklasse.

Späte Bindung und Botschaftenkonzept

Mit Hilfe der späten Bindung wurde das Botschaftenkonzept der OOP realisiert. Nun können wir - gedanklich - auf ein beliebiges Objekt zeigen und sagen: “drucke dich”. Die Vorstellung, daß Objekte ein Eigenleben haben und die richtige Reaktion auf eine solche Anweisung selbst herausfinden, hilft gekapselte Systeme zu entwickeln.



Seite 196    Einsteigerseminar C++

Vielleicht ließe sich dieses Konzept auch auf andere Sprachen übertragen. Viele professionelle Programmierer haben auch bisher schon ihre Datentypen sauber in Informationsdateien definiert. In der Entwicklung ist es aber wichtig, daß der Compiler nun die richtige Verwendung überprüft und einige Dienstleistungen zur Verfügung stellt. Auch bisher kannte man Sprungtabellen für Funktionen. Neu ist hier, daß der Compiler garantiert, daß in einer solchen Tabelle alle Einträge besetzt sind und kein Indexüberlauf möglich ist.


polydemogem

Das Vorbild - Atomium in Brüssel

Beim Botschaftenkonzept fällt mit immer das Atomium in Brüssel ein. Einzelne Objekte im Raum werden durch Kommunikationskanäle verbunden. Die Steuerung des Gesamtsystems geschieht durch den Austausch von Botschaften.


Es ist sicher kein Zufall, daß an vielen Stellen in der Welt versucht wird, dieses Konzept in neuen Multi-CPU-Maschinen oder in netzbasierten Clustern zu realisieren. Die gesunkenen Hardwarepreise machen solche verteilten Systeme möglich. (So arbeiten z. B. die Erfinder von UNIX oder Andrew Tanenbaum, der Entwickler von MINIX, daran.)


Späte Bindung oder Polymorphismus    Seite 197

Hinweis zur Benutzung der Vererbung mit Polymorphie

Im Kapitel über Vererbung haben wir über den logisch sinnvollen Aufbau von Klassen gesprochen. Im Zusammenhang mit der Polymorphie spielt die Vererbung eine leicht geänderte Rolle. Es muß hier nicht zwingend nach einer logischen Beziehung zwischen den Klassen gesucht werden.


Die Vererbung stellt hier einfach die Mechanik zur Verfügung, um die Polymorphie zu realisieren.

Hinweise zur Weiterarbeit

1) Schreiben Sie einen kleinen Autosimulator. Die Basisklasse sei “Auto”. Die abgeleiteten Klassen heißen wie die Autohersteller. Die “virtual”-Methode soll “beschleunige()” sein. Jede “beschleunige()”-Methode gibt die nach 10 s erreichte Geschwindigkeit aus. Legen Sie entsprechende Objekte an. Rufen Sie für alle Objekte die “virtual”-Methode mit später Bindung.


2) Suchen Sie eigene Klassenhierarchien mit gemeinsamen “virtual”-Methoden.


3) Definieren Sie eine Klasse “Weltzeit”, die die Greenwich Meantime (GMT/UTC/Zulu) verwalten soll. Leiten Sie davon mehrere Klassen ab, die jeweils eine Zeitzone (MEZ u.a.) repräsentieren. Schreiben Sie für alle Klassen die Methode “GibZeit()”, die die Ortszeit ausgibt. Verwenden Sie wieder die Polymorphie.  


4) Welche Roll spielte eigentlich “grafik” außer, daß es eine Basisklasse ist? Ersetzen Sie die “grafik”-Klasse durch eine allgemeine Basisklasse, die nur die Methoden deklariert, die “virtual” sein sollen. Schließen Sie die Deklaration nicht sofort mit einem “;” ab, sondern fügen Sie am Ende ein “ = 0;” an. Solchermaßen deklarierte Methoden sind leer; sie können nicht in der eigenen Klassen definiert werden, sondern müssen in abgeleiteten Klassen jeweils definiert werden (pure virtual functions).


Seite 198    Einsteigerseminar C++

Welche Vorteile bietet dieses Verfahren ?

Im nächsten Kapitel

Nachdem nun alle Grundelemente der OOP besprochen sind, können wir uns noch einem Problem der Softwareerstellung zuwenden. Die Frage ist, was muß man tun, um den großen Vorrat an C-Funktionen auch in C++ weiterverwenden zu können?


Das nächste Kapitel ist daher der mehrsprachigen Programmierung gewidmet. Funktionen, die in C geschrieben wurden sollen nun in C++ weiterverwendet werden können.



Späte Bindung oder Polymorphismus    Seite 199