Dynamische Typinformation
Die Vererbung brachte die Möglichkeit mit sich, Basisklassenzeigern Adressen beliebiger Objekte zuzuweisen, die von dieser Basisklasse abgeleitet wurden.
Die Basisklassenzeiger sind somit in einem begrenztem Umfang allgemeine Zeiger, die auf Objekte unterschiedlichen Typs zeigen können, sofern die Typen voneinander abgeleitet wurden. Steht uns nun in einem Modul ein Basisklassenzeiger zur Verfügung, dann kann damit zur Laufzeit nacheinander auf unterschiedliche Typen von Objekten verwiesen werden.
Um nun dynamisch herauszufinden, auf welchen Typ der Basisklassenzeiger aktuell zeigt, wurde in C++ der RTTI-Mechanismus aufgenommen. RTTI steht für "runtime type information" (Laufzeit Typ Information). Dieser Mechanismus erlaubt eine Abfrage des Typs eines Objektes im Code.
Implizite Typverwendung
Bisher haben wir bei der Polymorphie gesehen, daß innerhalb eines polymorphen Objektes die Typinformation gespeichert wurde. Bei jeder Verwendung einer polymorphen Methode wird diese Typinformation zur Laufzeit ausgewertet und zur Selektion der zum Typ passenden virtuellen Methode benutzt. Mit der Polymorphie wurde der Programmierer auf eine elegante Art von einer Typverwaltung befreit.
Einer der großen Vorteile der Polymorphie ist die automatische Erweiterbarkeit. Definiert der Programmierer eine neue Klasse mit Methoden, die virtuelle Basismethoden überlagern, muß am restlichen Code nichts geändert werden.
|
Dynamische Typinformation Seite 293 |
Wann immer möglich, sollte jeder Programmierer daher die Verwendung der Polymorphie erwägen. Die folgende, explizite Typinformation steht der Idee der Polymorphie gegenüber und kann dazu führen, daß die Einfachheit und Wartbarkeit der Polymorphie unterlaufen wird.
Dynamische Ermittlung des Typs
Die Grenze ihrer Anwendung findet die Polymorphie dann, wenn es gilt, in einem Programm mit Hilfe eines Basisklassenzeigers klassenspezifische Methoden zu verwenden, die nicht polymorph überlagert sind.
Rahmen1
|
Seite 294 Einsteigerseminar C++ |
In diesem Fall benötigt der Code eine Unterscheidungsmöglichkeit, auf welchen Typ gerade mit einem allgemeinen Zeiger verwiesen wird. Für derartige Fälle wurde eine dynamische Abfrage des Typs eines Objektes eingeführt.
Das Typinformationssystem wird mit Hilfe der Schlüsselwörter "dynamic_cast" und "typeid" benutzt. Weiter werden Objekte der Klasse "type_info" erzeugt und abgefragt. Eine Übersicht bietet Bild 19-1.
Typinformation mit "type_info"
Das Laufzeitsystem stellt dem Anwender nur eine begrenzte Informationsmöglichkeit über den Typ einer Variablen zur Verfügung. Die Information wird als Objekt der Klasse "type_info" bereitgestellt.
Die Klasse "type_info" stellt dem Anwender Vergleichsoperatoren zur Verfügung (== und !=). Damit kann der Programmierer herausfinden, ob zwei Typabfragen zu identischen oder unterschiedlichen Typen geführt haben. Verglichen werden dabei zwei Objekte der Klasse "type_info". Die Rückgabetypen der Vergleichsfunktionen verwenden den neuen Datentyp "bool", der die beiden Werte "false" und "true" annehmen kann.
Weiter existiert eine Elementfunktionen, die einen lexikalischen (also dem Namen nach) Vergleich zweier Typnamen ermöglicht ("before()"). Der Vergleich der Typnamen geschieht auf Grund des im Compiler verwendeten Zeichensatzes und liefert damit Ergebnisse, die nicht auf allen Systemen gleich sein müssen. Den Namen des Datentyps ermittelt "name()" (Bild 19-2).
Eine Zuweisung wurde für den Anwender durch die Deklaration eines privaten Zuweisungsoperators für den Anwender ausgeschlossen.
|
Dynamische Typinformation Seite 295 |
Rahmen3
Typermittlung mit "typeid"
Objekte des Typs "type_info" werden mit Hilfe des Schlüsselwortes ""typeid" generiert. Genauer liefert "typeid" eine Referenz auf ein konstantes "type_info"-Objekt. "Typeid" ist ist auf Objekte, Variable, Ausdrücke und Typnamen anwendbar.
|
Seite 296 Einsteigerseminar C++ |
"Typeid" zeigt ein dynamisches Verhalten. Ist der Operand von "typeid" ein dereferenzierter Basisklassenzeiger, ein Feldzugriff oder eine Referenz, dann liefert "typeid" die Information, auf wen wir uns tatsächlich beziehen.
Ist der Ausdruck, den "typeid" prüft, weder ein Zeiger noch eine Referenz auf ein Objekt einer polymorphen Klasse, dann liefert "typeid" die statische Typinformation zurück.
Wollen wir "typeid" verwenden, benötigen wir Informationen zur Klasse "type_info". Diese finden wir in der Informationsdatei "typeinfo.h", die wir einlesen müssen (Bild 19-2).
Einen Sonderfall stellt die Abfrage mit einem ungültigen Zeiger dar. Hier wird eine Fehlervariable des Typs "bad_typeid" ausgeworfen.
Die Ausgabe des Beispielprogramms findet sich im Bild 19-3.
Rahmen5
Dynamische Typkonvertierung
Den Operator "dynamic_cast "haben wir im Rahmen der allgemeinen Typkonvertierung schon kurz gesehen. Im Zusammenhang mit der Besprechung des Laufzeittypsystems wollen wir seine Aufgaben noch einmal betrachten.
Konvertierung von Zeigern auf Objekte kennen wir bisher in einer Form, die die Adresse eines gegebenen Objektes in einem allgemeinen Basisklassenzeiger speichern kann. Hier findet eine Typkonvertierung eines Zeigers auf ein abgeleitetes Objekt hin zu einem Zeiger auf die Basisklasse statt. Dies ist eine statische, vom Compiler durchgeführte Typkonvertierung.
|
Dynamische Typinformation Seite 297 |
Man spricht im Englischen von einem "up cast" (nach oben, logisch allgemeiner).
Die andere Richtung hin zu spezielleren Objekten ("down cast" ) ist nicht so einfach. Hat man einen Zeiger auf ein Objekt der Basisklasse und weist ihm die Adresse eines Objektes einer abgeleiteten Klasse zu, dann kann man mit diesem Zeiger nicht Methoden der abgeleiteten Klasse aufrufen. Ausgenommen davon sind natürlich polymorphe Methoden, die über einen internen Mechanismus gefunden werden.
Wenn der Programmierer mit Hilfe eines Basisklassenzeigers nicht-polymorphe Methoden einer abgeleiteten Klasse aufrufen können will, benötigt er eine dynamische Konvertierung des Zeigers mit Hilfe von "dynamic_cast".
"Dynamic_cast" erwartet als Quellinformation einen Zeiger oder eine Referenz auf ein Objekt, dessen Typ Teil einer Klassenhierarchie ist. Nur dann stehen in den Objekten Typinformationen zur Verfügung.
Kann keine Typkonvertierung durchgeführt werden, liefert der "dynamic_cast"-Operator eine ungültige Adresse, wenn das Ziel ein Zeiger ist, oder er wirft eine Fehlervariable des Typs "bad_cast" aus, falls das Ziel eine Referenz ist.
Im Beispielprogramm (Bild 19-4) ist die typische Verwendung gezeigt. Mit Hilfe des "dynamic_cast"-Operators wird nun ein "down cast", eine Typwandlung hin zu spezielleren Klassen durchgeführt.
Der Operator vereint in sich zwei Eigenschaften: Er prüft, ob eine Wandlung überhaupt möglich ist und liefert, falls eine Typwandlung sinnvoll ist, den gewandelten Typ zurück. Daß die Prüfung ein wichtiger Bestandteil ist, haben wir im Kapitel über allgemeine Typkonvertierungen bei der Gegenüberstellung der statischen (vom Compiler durchgeführten) Wandlung mit "static_cast" und der Überprüfung zur Laufzeit mit "dynamic_cast" gesehen.
|
Seite 298 Einsteigerseminar C++ |
Man kann sich die Wirkung einfach vorstellen. Zeigt ein Basisklassenzeiger auf ein abgeleitetes Objekt, kann die Rückgabe auf dieses Objekt oder eingeschlossene Objekte zeigen. Voraussetzung dabei ist, daß der Zieltyp nicht eine weiter abgeleitete Klasse (z. B. dritte Ableitung) beschreibt als das momentan bezogene Objekt als Typ hat.
Das Beispielprogramm verwendet eine kleine Besonderheit des Borland Compilers 4.5: Die Fehlervariable hat hier den Typ "Bad_cast" anstelle von "bad_cast". Dies ist keine Unsauberkeit des Compilers, da zum Erscheinungsdatum dieses Compilers die Festlegungen im Standard noch gar nicht abgeschlossen waren. (Andere Compilerhersteller haben sich erst gar nicht bemüht, die neueren Details schnell einzubauen.)
Im Beispiel werden zwei dynamische Konvertierungen versucht. Die erste wird dabei erfolgreich sein, die zweite fehlschlagen. Die Fangfunktionen werden nicht verwendet, da bei einer Zeigerkonvertierung keine Fehlervariable ausgeworfen wird.
Mit den drei besprochenen Elementen kann der Programmierer auf die Typinformation zugreifen, die in polymorphen Objekten abgelegt ist. Diese Informationen werden aber in vielen Fällen Änderungen unterliegen, die mit jeder neu definierten Klasse im Typsystem auftauchen können. Die Verwendung sollte daher so weit wie möglich zu Gunsten eines polymorphen Aufrufs eingeschränkt werden.
|
Dynamische Typinformation Seite 299 |
Rahmen7
(Fortsetzung nächste Seite)
|
Seite 300 Einsteigerseminar C++ |
Im nächsten Kapitel
In den vergangenen Kapiteln haben wir einen Streifzug durch die Welt von C++ gemacht..
Die vielen Chancen und Möglichkeiten von C++ haben wir uns erarbeiten müssen. Auch C++ steht ja in der Tradition von C, einer Sprache, die mit wenigen Schlüsselwörtern viel ausdrücken konnte. Der Preis für die Einfachheit war, Verständnis aufzubauen für die Elemente der Sprache und der Objekt-orientierten Programmierung.
Und damit sind wir beim Nachwort angelangt.
|
Dynamische Typinformation Seite 301 |
|
Seite 302 Einsteigerseminar C++ |