Menu
Coddy logo textTech

Conversion dynamique et 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 à l’exécution ou convertir en toute sécurité un pointeur de classe de base en pointeur de classe dérivée. C++ fournit la RTTI (Runtime Type Information) et dynamic_cast pour ces situations.

dynamic_cast convertit en toute sécurité les pointeurs ou les références au sein d’une hiérarchie d’héritage. Contrairement à static_cast, il 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; }
};

class Cat : public Animal {};

Animal* animal = new Dog();
Dog* dog = dynamic_cast<Dog*>(animal);  // Réussit : renvoie un pointeur valide

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

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

Important : dynamic_cast ne fonctionne qu’avec des types polymorphes (des classes comportant 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 infos de type pour Dog

Bien que dynamic_cast soit utile, son utilisation fréquente indique souvent un problème de conception. Préfère les fonctions virtuelles lorsque c’est possible, car elles permettent à l’objet de gérer le comportement spécifique au type sans vérification explicite du type.

challenge icon

Défi

Facile

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

Vous allez organiser votre code sur trois fichiers :

  • Vehicle.h : définir une classe de base Vehicle qui représente n’importe quel véhicule du système :
    • un membre std::string licensePlate protégé
    • un constructeur qui initialise la plaque d’immatriculation
    • une méthode virtuelle getDescription() qui renvoie "Vehicle: <licensePlate>"
    • un destructeur virtuel
  • Vehicles.h : définir trois types de véhicules dérivés :

    Car :

    • un membre int seatCount privé
    • un constructeur prenant la plaque d’immatriculation et le nombre de sièges
    • redéfinir getDescription() pour renvoyer "Car: <licensePlate>"
    • une méthode inspectSeatbelts() qui affiche : Inspecting <seatCount> seatbelts in <licensePlate>

    Truck :

    • un membre double cargoCapacity privé (en tonnes)
    • un constructeur prenant la plaque d’immatriculation et la capacité de chargement
    • redéfinir getDescription() pour renvoyer "Truck: <licensePlate>"
    • une méthode inspectCargo() qui affiche : Inspecting cargo area (<cargoCapacity> tons) in <licensePlate>

    Motorcycle :

    • un membre bool hasSidecar privé
    • un constructeur prenant la plaque d’immatriculation et l’indication de la présence d’un side-car
    • redéfinir getDescription() pour renvoyer "Motorcycle: <licensePlate>"
    • une méthode inspectHelmetStorage() qui affiche : Inspecting helmet storage in <licensePlate> s’il possède un side-car, ou No helmet storage in <licensePlate> s’il n’en possède pas
  • main.cpp : lire trois entrées (chacune sur une ligne distincte) :
    1. la plaque d’immatriculation de la voiture
    2. la plaque d’immatriculation du camion
    3. la plaque d’immatriculation de la moto

    Créer une Car avec 4 sièges, un Truck d’une capacité de 10.5 tonnes et une Motorcycle avec un side-car. Stocker les trois véhicules dans un tableau de pointeurs Vehicle*.

    Parcourir le tableau et, pour chaque véhicule :

    1. afficher sa description à l’aide de getDescription()
    2. utiliser dynamic_cast pour tenter une conversion vers chaque type dérivé
    3. si la conversion vers Car* réussit, appeler inspectSeatbelts()
    4. si la conversion vers Truck* réussit, appeler inspectCargo()
    5. si la conversion vers Motorcycle* réussit, appeler inspectHelmetStorage()

    Afficher une ligne vide entre les inspections de chaque véhicule. Libérer les 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 que dynamic_cast renvoie un pointeur valide uniquement lorsque le type réel de l’objet correspond au type cible. Pour chaque véhicule, une seule des trois conversions réussira, ce qui vous permettra d’appeler en toute sécurité la méthode d’inspection spécifique au type. C’est toute la puissance de la RTTI : déterminer le type réel au moment de l’exécution et agir en conséquence.

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 side-car (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 de caster vers chaque type dérivé
    //   3. Si le cast vers Car* réussit, appeler inspectSeatbelts()
    //   4. Si le cast vers Truck* réussit, appeler inspectCargo()
    //   5. If cast to Motorcycle* succeeds, call inspectHelmetStorage()
    //   6. Afficher une ligne vide entre l'inspection de chaque véhicule

    // TODO: Nettoyer 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

Entraînez-vous par vous-même : Compilateur C++ en ligne