Menu
Coddy logo textTech

Rzutowanie dynamiczne i RTTI

Część sekcji Programowanie obiektowe ścieżki C++ w Coddy. Lekcja 62 z 104.

Czasami podczas pracy z polimorfizmem trzeba określić rzeczywisty typ obiektu w czasie wykonywania programu lub bezpiecznie przekonwertować wskaźnik do klasy bazowej na wskaźnik do klasy pochodnej. C++ udostępnia RTTI (informacje o typie w czasie wykonywania) oraz dynamic_cast w takich sytuacjach.

dynamic_cast bezpiecznie konwertuje wskaźniki lub referencje w hierarchii dziedziczenia. W przeciwieństwie do static_cast wykonuje kontrolę w czasie działania i zwraca nullptr, jeśli konwersja jest nieprawidłowa:

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);  // Udaje się: zwraca prawidłowy wskaźnik

if (dog) {
    dog->bark();  // Można bezpiecznie wywołać metodę specyficzną dla Dog
}

Cat* cat = dynamic_cast<Cat*>(animal);  // Nie udaje się: zwraca nullptr

Ważne: dynamic_cast działa tylko z typami polimorficznymi (klasami mającymi co najmniej jedną funkcję wirtualną). Operator typeid umożliwia sprawdzenie rzeczywistego typu obiektu:

#include <typeinfo>

Animal* pet = new Dog();
std::cout << typeid(*pet).name() << std::endl;  // Wyświetla informacje o typie Dog

Chociaż dynamic_cast jest przydatny, jego częste używanie często wskazuje na problem z projektem. Jeśli to możliwe, preferuj funkcje wirtualne, ponieważ pozwalają obiektowi obsługiwać zachowanie zależne od typu bez jawnego sprawdzania typu.

challenge icon

Wyzwanie

Łatwy

Zbudujmy system kontroli pojazdów, który używa dynamic_cast do bezpiecznego identyfikowania różnych typów pojazdów i interakcji z nimi. Utworzysz hierarchię pojazdów, w której inspektor musi przeprowadzać kontrole specyficzne dla typu, obsługiwane tylko przez niektóre pojazdy.

Rozmieścisz kod w trzech plikach:

  • Vehicle.h: Zdefiniuj bazową klasę Vehicle, która reprezentuje dowolny pojazd w systemie:
    • Pole std::string licensePlate o dostępie chronionym
    • Konstruktor inicjalizujący numer rejestracyjny
    • Wirtualna metoda getDescription(), która zwraca "Vehicle: <licensePlate>"
    • Wirtualny destruktor
  • Vehicles.h: Zdefiniuj trzy pochodne typy pojazdów:

    Car:

    • Prywatne pole int seatCount
    • Konstruktor przyjmujący numer rejestracyjny i liczbę miejsc
    • Nadpisz getDescription(), aby zwracała "Car: <licensePlate>"
    • Metoda inspectSeatbelts(), która wyświetla: Inspecting <seatCount> seatbelts in <licensePlate>

    Truck:

    • Prywatne pole double cargoCapacity (w tonach)
    • Konstruktor przyjmujący numer rejestracyjny i ładowność
    • Nadpisz getDescription(), aby zwracała "Truck: <licensePlate>"
    • Metoda inspectCargo(), która wyświetla: Inspecting cargo area (<cargoCapacity> tons) in <licensePlate>

    Motorcycle:

    • Prywatne pole bool hasSidecar
    • Konstruktor przyjmujący numer rejestracyjny i informację o obecności bocznego wózka
    • Nadpisz getDescription(), aby zwracała "Motorcycle: <licensePlate>"
    • Metoda inspectHelmetStorage(), która wyświetla Inspecting helmet storage in <licensePlate>, jeśli pojazd ma boczny wózek, lub No helmet storage in <licensePlate>, jeśli go nie ma
  • main.cpp: Wczytaj trzy dane wejściowe (każde w osobnym wierszu):
    1. Numer rejestracyjny samochodu
    2. Numer rejestracyjny ciężarówki
    3. Numer rejestracyjny motocykla

    Utwórz obiekt Car z 4 miejscami, obiekt Truck o ładowności 10.5 ton oraz obiekt Motorcycle z bocznym wózkiem. Umieść wszystkie trzy w tablicy wskaźników Vehicle*.

    Przejdź przez tablicę i dla każdego pojazdu:

    1. Wyświetl jego opis za pomocą getDescription()
    2. Użyj dynamic_cast, aby spróbować rzutować wskaźnik na każdy z typów pochodnych
    3. Jeśli rzutowanie na Car* się powiedzie, wywołaj inspectSeatbelts()
    4. Jeśli rzutowanie na Truck* się powiedzie, wywołaj inspectCargo()
    5. Jeśli rzutowanie na Motorcycle* się powiedzie, wywołaj inspectHelmetStorage()

    Wyświetl pusty wiersz między kontrolą każdego pojazdu. Po zakończeniu zwolnij dynamicznie przydzielone obiekty.

Na przykład dla danych wejściowych ABC-123, TRK-456 i 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

Zwróć uwagę, że dynamic_cast zwraca prawidłowy wskaźnik tylko wtedy, gdy rzeczywisty typ obiektu jest zgodny z typem docelowym. Dla każdego pojazdu powiedzie się tylko jedno z trzech rzutowań, co pozwala bezpiecznie wywołać metodę kontroli właściwą dla danego typu. Na tym polega siła RTTI: ustalaniu rzeczywistego typu w czasie działania programu i odpowiednim działaniu.

Spróbuj swoich sił

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

using namespace std;

int main() {
    // Wczytaj dane wejściowe
    string carPlate, truckPlate, motorcyclePlate;
    cin >> carPlate;
    cin >> truckPlate;
    cin >> motorcyclePlate;

    // TODO: Utwórz obiekt Car z 4 miejscami
    // TODO: Utwórz obiekt Truck o ładowności 10.5 tony
    // TODO: Utwórz obiekt Motorcycle z wózkiem bocznym (true)

    // TODO: Umieść wszystkie trzy obiekty w tablicy wskaźników Vehicle*

    // TODO: Przejdź przez tablicę i dla każdego pojazdu:
    //   1. Wypisz jego opis za pomocą getDescription()
    //   2. Użyj dynamic_cast, aby spróbować rzutowania na każdy typ pochodny
    //   3. Jeśli rzutowanie na Car* się powiedzie, wywołaj inspectSeatbelts()
    //   4. Jeśli rzutowanie na Truck* się powiedzie, wywołaj inspectCargo()
    //   5. Jeśli rzutowanie na Motorcycle* się powiedzie, wywołaj inspectHelmetStorage()
    //   6. Wypisz pustą linię między kontrolami kolejnych pojazdów

    // TODO: Zwolnij pamięć zajmowaną przez dynamicznie alokowane obiekty

    return 0;
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator C++ online