Menu
Coddy logo textTech

Dynamic Casting & RTTI

Fait partie de la section Programmation Orientée Objet du Journey C++ de Coddy — leçon 62 sur 104.

Parfois, lorsque vous travaillez avec le polymorphisme, vous devez déterminer le type réel d'un objet au moment de l'exécution ou convertir en toute sécurité un pointeur de classe de base en un pointeur de classe dérivée. Le C++ fournit le RTTI (Runtime Type Information) et dynamic_cast pour ces situations.

dynamic_cast convertit en toute sécurité des pointeurs ou des références au sein d'une hiérarchie d'héritage. Contrairement à static_cast, il effectue une vérification à l'exécution et retourne nullptr si la conversion est invalide :

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);  // Réussit : retourne un pointeur valide

if (dog) {
    dog->bark();  // Sûr d'appeler une méthode spécifique à Dog
}

Cat* cat = dynamic_cast<Cat*>(animal);  // Échoue : retourne nullptr

Important : dynamic_cast ne fonctionne qu'avec des types polymorphes (classes avec au moins une fonction virtuelle). L'opérateur typeid vous permet d'interroger le type réel d'un objet :

#include <typeinfo>

Animal* pet = new Dog();
std::cout << typeid(*pet).name() << std::endl;  // Affiche les informations de type pour Dog

Bien que dynamic_cast soit utile, une utilisation fréquente indique souvent un problème de conception. Privilégiez les fonctions virtuelles quand cela est possible, car elles permettent à l'objet de gérer le comportement spécifique au type sans vérification de type explicite.

challenge icon

Défi

Facile

Construisons un système d'inspection de véhicules qui utilise dynamic_cast pour identifier et interagir en toute sécurité avec différents types de véhicules. Vous allez créer une hiérarchie de véhicules où un inspecteur doit effectuer des vérifications spécifiques au type que seuls certains véhicules prennent en charge.

Vous organiserez votre code sur trois fichiers :

  • Vehicle.h : Définissez une classe de base Vehicle qui représente n'importe quel véhicule dans le système :
    • Un membre protégé std::string licensePlate
    • Un constructeur qui initialise la plaque d'immatriculation
    • Une méthode virtuelle getDescription() qui retourne "Vehicle: <licensePlate>"
    • Un destructeur virtuel
  • Vehicles.h : Définissez trois types de véhicules dérivés :

    Car :

    • Un membre privé int seatCount
    • Un constructeur prenant la plaque d'immatriculation et le nombre de sièges
    • Surchargez getDescription() pour retourner "Car: <licensePlate>"
    • Une méthode inspectSeatbelts() qui affiche : Inspecting <seatCount> seatbelts in <licensePlate>

    Truck :

    • Un membre privé double cargoCapacity (en tonnes)
    • Un constructeur prenant la plaque d'immatriculation et la capacité de chargement
    • Surchargez getDescription() pour retourner "Truck: <licensePlate>"
    • Une méthode inspectCargo() qui affiche : Inspecting cargo area (<cargoCapacity> tons) in <licensePlate>

    Motorcycle :

    • Un membre privé bool hasSidecar
    • Un constructeur prenant la plaque d'immatriculation et le statut du side-car
    • Surchargez getDescription() pour retourner "Motorcycle: <licensePlate>"
    • Une méthode inspectHelmetStorage() qui affiche : Inspecting helmet storage in <licensePlate> s'il a un side-car, ou No helmet storage in <licensePlate> s'il n'en a pas
  • main.cpp : Lisez trois entrées (chacune sur une ligne séparée) :
    1. Plaque d'immatriculation de la voiture
    2. Plaque d'immatriculation du camion
    3. Plaque d'immatriculation de la moto

    Créez une Car avec 4 sièges, un Truck avec une capacité de 10.5 tonnes, et une Motorcycle avec un side-car. Stockez les trois dans un tableau de pointeurs Vehicle*.

    Bouclez sur le tableau et pour chaque véhicule :

    1. Affichez sa description en utilisant getDescription()
    2. Utilisez dynamic_cast pour tenter un transtypage vers chaque type dérivé
    3. Si le transtypage vers Car* réussit, appelez inspectSeatbelts()
    4. Si le transtypage vers Truck* réussit, appelez inspectCargo()
    5. Si le transtypage vers Motorcycle* réussit, appelez inspectHelmetStorage()

    Affichez une ligne vide entre l'inspection de chaque véhicule. Nettoyez vos objets alloués dynamiquement une fois terminé.

Par exemple, avec les entrées ABC-123, TRK-456, et 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

Remarquez comment dynamic_cast ne renvoie un pointeur valide que lorsque le type réel de l'objet correspond au type cible. Pour chaque véhicule, un seul des trois transtypages réussira, vous permettant d'appeler en toute sécurité la méthode d'inspection spécifique au type. C'est la puissance du RTTI — déterminer le type réel au moment de l'exécution et agir en conséquence.

Aide-mémoire

Le C++ fournit le RTTI (Runtime Type Information) et dynamic_cast pour déterminer le type réel d'un objet au moment de l'exécution et convertir en toute sécurité les pointeurs au sein d'une hiérarchie d'héritage.

dynamic_cast effectue une vérification à l'exécution et renvoie nullptr si la conversion est invalide :

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);  // Réussit : renvoie un pointeur valide

if (dog) {
    dog->bark();  // Sûr d'appeler une méthode spécifique à Dog
}

Cat* cat = dynamic_cast<Cat*>(animal);  // Échoue : renvoie nullptr

Important : dynamic_cast ne fonctionne qu'avec des types polymorphes (classes ayant au moins une fonction virtuelle).

L'opérateur typeid interroge le type réel d'un objet :

#include <typeinfo>

Animal* pet = new Dog();
std::cout << typeid(*pet).name() << std::endl;  // Affiche les informations de type pour Dog

Bien qu'utile, l'utilisation fréquente de dynamic_cast indique souvent un problème de conception. Préférez les fonctions virtuelles lorsque cela est possible.

Essayez vous-même

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

using namespace std;

int main() {
    // Lire les entrées
    string carPlate, truckPlate, motorcyclePlate;
    cin >> carPlate;
    cin >> truckPlate;
    cin >> motorcyclePlate;

    // TODO: Créer une Car avec 4 sièges
    // TODO: Créer un Truck avec une capacité de 10.5 tonnes
    // TODO: Créer une Motorcycle avec un sidecar (true)

    // TODO: Stocker les trois dans un tableau de pointeurs Vehicle*

    // TODO: Parcourir le tableau et pour chaque véhicule :
    //   1. Afficher sa description en utilisant getDescription()
    //   2. Utiliser dynamic_cast pour tenter un transtypage vers chaque type dérivé
    //   3. Si le transtypage vers Car* réussit, appeler inspectSeatbelts()
    //   4. Si le transtypage vers Truck* réussit, appeler inspectCargo()
    //   5. Si le transtypage vers Motorcycle* réussit, appeler inspectHelmetStorage()
    //   6. Afficher une ligne vide entre l'inspection de chaque véhicule

    // TODO: Libérer les objets alloués dynamiquement

    return 0;
}
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation Orientée Objet