Menu
Coddy logo textTech

Dynamic Casting & RTTI

Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy. Lektion 62 von 104.

Manchmal musst du bei der Arbeit mit Polymorphie den tatsächlichen Typ eines Objekts zur Laufzeit bestimmen oder einen Zeiger auf eine Basisklasse sicher in einen Zeiger auf eine abgeleitete Klasse umwandeln. C++ stellt für diese Situationen RTTI (Runtime Type Information) und dynamic_cast bereit.

dynamic_cast konvertiert Zeiger oder Referenzen innerhalb einer Vererbungshierarchie sicher. Anders als static_cast führt es eine Laufzeitprüfung durch und gibt nullptr zurück, wenn die Konvertierung ungültig ist:

class Animal {
public:
    virtual ~Animal() = default;
};

class Dog : public Animal {
public:
    void bark() { std::cout << "Woof!" << std::endl; }
};

class Cat : public Animal {};

Animal* animal = new Dog();
Dog* dog = dynamic_cast<Dog*>(animal);  // Erfolgreich: gibt gültigen Zeiger zurück

if (dog) {
    dog->bark();  // Sicher, die Dog-spezifische Methode aufzurufen
}

Cat* cat = dynamic_cast<Cat*>(animal);  // Schlägt fehl: gibt nullptr zurück

Wichtig: dynamic_cast funktioniert nur mit polymorphen Typen (Klassen mit mindestens einer virtuellen Funktion). Mit dem Operator typeid kannst du den tatsächlichen Typ eines Objekts abfragen:

#include <typeinfo>

Animal* pet = new Dog();
std::cout << typeid(*pet).name() << std::endl;  // Gibt Typinfo für Dog aus

Obwohl dynamic_cast nützlich ist, weist eine häufige Verwendung oft auf ein Designproblem hin. Bevorzuge nach Möglichkeit virtuelle Funktionen, da sie dem Objekt ermöglichen, typspezifisches Verhalten ohne explizite Typprüfung zu handhaben.

challenge icon

Aufgabe

Einfach

Erstellen wir ein Fahrzeuginspektionssystem, das dynamic_cast verwendet, um verschiedene Fahrzeugtypen sicher zu identifizieren und mit ihnen zu interagieren. Du erstellst eine Hierarchie von Fahrzeugen, in der ein Inspektor typspezifische Prüfungen durchführen muss, die nur bestimmte Fahrzeuge unterstützen.

Du organisierst deinen Code über drei Dateien:

  • Vehicle.h: Definiere eine Vehicle-Basisklasse, die jedes Fahrzeug im System repräsentiert:
    • Ein geschütztes std::string licensePlate-Member
    • Einen Konstruktor, der das Kennzeichen initialisiert
    • Eine virtuelle getDescription()-Methode, die "Vehicle: <licensePlate>" zurückgibt
    • Einen virtuellen Destruktor
  • Vehicles.h: Definiere drei abgeleitete Fahrzeugtypen:

    Car:

    • Ein privates int seatCount-Member
    • Einen Konstruktor, der Kennzeichen und Sitzanzahl entgegennimmt
    • Überschreibe getDescription(), sodass "Car: <licensePlate>" zurückgegeben wird
    • Eine Methode inspectSeatbelts(), die Folgendes ausgibt: Inspecting <seatCount> seatbelts in <licensePlate>

    Truck:

    • Ein privates double cargoCapacity-Member (in Tonnen)
    • Einen Konstruktor, der Kennzeichen und Ladekapazität entgegennimmt
    • Überschreibe getDescription(), sodass "Truck: <licensePlate>" zurückgegeben wird
    • Eine Methode inspectCargo(), die Folgendes ausgibt: Inspecting cargo area (<cargoCapacity> tons) in <licensePlate>

    Motorcycle:

    • Ein privates bool hasSidecar-Member
    • Einen Konstruktor, der Kennzeichen und den Beiwagenstatus entgegennimmt
    • Überschreibe getDescription(), sodass "Motorcycle: <licensePlate>" zurückgegeben wird
    • Eine Methode inspectHelmetStorage(), die Inspecting helmet storage in <licensePlate> ausgibt, wenn das Fahrzeug einen Beiwagen hat, oder No helmet storage in <licensePlate>, wenn es keinen hat
  • main.cpp: Lies drei Eingaben ein (jeweils in einer eigenen Zeile):
    1. Kennzeichen des Autos
    2. Kennzeichen des Lastwagens
    3. Kennzeichen des Motorrads

    Erstelle ein Car mit 4 Sitzen, einen Truck mit einer Kapazität von 10.5 Tonnen und ein Motorcycle mit einem Beiwagen. Speichere alle drei in einem Array aus Vehicle*-Zeigern.

    Durchlaufe das Array und führe für jedes Fahrzeug Folgendes aus:

    1. Gib seine Beschreibung mithilfe von getDescription() aus
    2. Verwende dynamic_cast, um eine Umwandlung in jeden abgeleiteten Typ zu versuchen
    3. Wenn die Umwandlung in Car* erfolgreich ist, rufe inspectSeatbelts() auf
    4. Wenn die Umwandlung in Truck* erfolgreich ist, rufe inspectCargo() auf
    5. Wenn die Umwandlung in Motorcycle* erfolgreich ist, rufe inspectHelmetStorage() auf

    Gib zwischen den Inspektionen der einzelnen Fahrzeuge eine Leerzeile aus. Räume die dynamisch reservierten Objekte anschließend auf.

Bei den Eingaben ABC-123, TRK-456 und MTR-789 sieht das Ergebnis beispielsweise so aus:

Car: ABC-123
Inspecting 4 seatbelts in ABC-123

Truck: TRK-456
Inspecting cargo area (10.5 tons) in TRK-456

Motorcycle: MTR-789
Inspecting helmet storage in MTR-789

Beachte, wie dynamic_cast nur dann einen gültigen Zeiger zurückgibt, wenn der tatsächliche Objekttyp mit dem Zieltyp übereinstimmt. Für jedes Fahrzeug ist nur eine der drei Umwandlungen erfolgreich, sodass du die typspezifische Inspektionsmethode sicher aufrufen kannst. Das ist die Stärke von RTTI: den tatsächlichen Typ zur Laufzeit zu bestimmen und entsprechend zu handeln.

Probier es selbst

#include <iostream>
#include <string>
#include "Vehicle.h"
#include "Vehicles.h"

using namespace std;

int main() {
    // Eingaben lesen
    string carPlate, truckPlate, motorcyclePlate;
    cin >> carPlate;
    cin >> truckPlate;
    cin >> motorcyclePlate;

    // TODO: Erstelle ein Car mit 4 Sitzen
    // TODO: Erstelle einen Truck mit 10.5 Tonnen Kapazität
    // TODO: Erstelle ein Motorcycle mit einem Beiwagen (true)

    // TODO: Speichere alle drei in einem Array von Vehicle*-Zeigern

    // TODO: Durchlaufe das Array und für jedes vehicle:
    //   1. Gib seine Beschreibung mit getDescription() aus
    //   2. Verwende dynamic_cast, um das Casting zu jedem abgeleiteten Typ zu versuchen
    //   3. Wenn das Casting zu Car* erfolgreich ist, rufe inspectSeatbelts() auf
    //   4. Wenn das Casting zu Truck* erfolgreich ist, rufe inspectCargo() auf
    //   5. If cast to Motorcycle* succeeds, call inspectHelmetStorage()
    //   6. Gib eine Leerzeile zwischen der Inspektion jedes Fahrzeugs aus

    // TODO: Räume dynamisch allozierte Objekte auf

    return 0;
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-C++-Compiler