Menu
Coddy logo textTech

Dynamic Casting & RTTI

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

Manchmal, wenn Sie mit Polymorphismus arbeiten, müssen Sie den tatsächlichen Typ eines Objekts zur Laufzeit bestimmen oder einen Basisklassen-Zeiger sicher in einen Zeiger einer abgeleiteten Klasse umwandeln. C++ bietet RTTI (Runtime Type Information) und dynamic_cast für diese Situationen.

dynamic_cast konvertiert sicher Zeiger oder Referenzen innerhalb einer Vererbungshierarchie. Im Gegensatz zu 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 einen gültigen Zeiger zurück

if (dog) {
    dog->bark();  // Sicherer Aufruf einer Dog-spezifischen Methode
}

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). Der typeid-Operator ermöglicht es Ihnen, den tatsächlichen Typ eines Objekts abzufragen:

#include <typeinfo>

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

Obwohl dynamic_cast nützlich ist, deutet eine häufige Verwendung oft auf ein Designproblem hin. Bevorzugen Sie virtuelle Funktionen, wann immer möglich, da sie es dem Objekt ermöglichen, typspezifisches Verhalten ohne explizite Typprüfung zu handhaben.

challenge icon

Aufgabe

Einfach

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

Sie werden Ihren Code auf drei Dateien verteilen:

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

    Car:

    • Ein privates int seatCount Mitglied
    • Ein Konstruktor, der das Kennzeichen und die Anzahl der Sitze entgegennimmt
    • Überschreiben Sie getDescription(), um "Car: <licensePlate>" zurückzugeben
    • Eine Methode inspectSeatbelts(), die Folgendes ausgibt: Inspecting <seatCount> seatbelts in <licensePlate>

    Truck:

    • Ein privates double cargoCapacity Mitglied (in Tonnen)
    • Ein Konstruktor, der das Kennzeichen und die Ladekapazität entgegennimmt
    • Überschreiben Sie getDescription(), um "Truck: <licensePlate>" zurückzugeben
    • Eine Methode inspectCargo(), die Folgendes ausgibt: Inspecting cargo area (<cargoCapacity> tons) in <licensePlate>

    Motorcycle:

    • Ein privates bool hasSidecar Mitglied
    • Ein Konstruktor, der das Kennzeichen und den Beiwagen-Status entgegennimmt
    • Überschreiben Sie getDescription(), um "Motorcycle: <licensePlate>" zurückzugeben
    • Eine Methode inspectHelmetStorage(), die Inspecting helmet storage in <licensePlate> ausgibt, wenn es einen Beiwagen hat, oder No helmet storage in <licensePlate>, wenn es keinen hat
  • main.cpp: Lesen Sie drei Eingaben ein (jede in einer separaten Zeile):
    1. Kennzeichen des Autos
    2. Kennzeichen des LKWs
    3. Kennzeichen des Motorrads

    Erstellen Sie ein Car mit 4 Sitzen, einen Truck mit 10.5 Tonnen Kapazität und ein Motorcycle mit einem Beiwagen. Speichern Sie alle drei in einem Array von Vehicle* Pointern.

    Iterieren Sie durch das Array und führen Sie für jedes Fahrzeug Folgendes aus:

    1. Geben Sie die Beschreibung mit getDescription() aus
    2. Verwenden Sie dynamic_cast, um zu versuchen, in jeden abgeleiteten Typ zu casten
    3. Wenn der Cast zu Car* erfolgreich ist, rufen Sie inspectSeatbelts() auf
    4. Wenn der Cast zu Truck* erfolgreich ist, rufen Sie inspectCargo() auf
    5. Wenn der Cast zu Motorcycle* erfolgreich ist, rufen Sie inspectHelmetStorage() auf

    Geben Sie zwischen jeder Fahrzeuginspektion eine Leerzeile aus. Bereinigen Sie Ihre dynamisch zugewiesenen Objekte, wenn Sie fertig sind.

Zum Beispiel mit den Eingaben ABC-123, TRK-456 und MTR-789:

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

Beachten Sie, wie dynamic_cast nur dann einen gültigen Pointer zurückgibt, wenn der tatsächliche Objekttyp mit dem Zieltyp übereinstimmt. Für jedes Fahrzeug wird nur einer der drei Casts erfolgreich sein, was es Ihnen ermöglicht, die typspezifische Inspektionsmethode sicher aufzurufen. Dies ist die Stärke von RTTI — den tatsächlichen Typ zur Laufzeit zu bestimmen und entsprechend zu handeln.

Spickzettel

C++ bietet RTTI (Runtime Type Information) und dynamic_cast, um den tatsächlichen Typ eines Objekts zur Laufzeit zu bestimmen und Zeiger innerhalb einer Vererbungshierarchie sicher zu konvertieren.

dynamic_cast führt 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; }
};

Animal* animal = new Dog();
Dog* dog = dynamic_cast<Dog*>(animal);  // Succeeds: returns valid pointer

if (dog) {
    dog->bark();  // Safe to call Dog-specific method
}

Cat* cat = dynamic_cast<Cat*>(animal);  // Fails: returns nullptr

Wichtig: dynamic_cast funktioniert nur mit polymorphen Typen (Klassen mit mindestens einer virtuellen Funktion).

Der typeid-Operator fragt den tatsächlichen Typ eines Objekts ab:

#include <typeinfo>

Animal* pet = new Dog();
std::cout << typeid(*pet).name() << std::endl;  // Outputs type info for Dog

Obwohl nützlich, deutet die häufige Verwendung von dynamic_cast oft auf ein Designproblem hin. Bevorzugen Sie nach Möglichkeit virtuelle Funktionen.

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*-Pointern

    // TODO: Durchlaufe das Array in einer Schleife und für jedes Fahrzeug:
    //   1. Gib dessen Beschreibung mit getDescription() aus
    //   2. Verwende dynamic_cast, um das Casting in jeden 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. Wenn das Casting zu Motorcycle* erfolgreich ist, rufe inspectHelmetStorage() auf
    //   6. Gib eine Leerzeile zwischen den Inspektionen der einzelnen Fahrzeuge aus

    // TODO: Bereinige die dynamisch zugewiesenen Objekte

    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