Logo HE-ARC Logo HES-SO

1242.2 Langage C++ - 2024-2025

Chapitre 5

Opérateurs de transtypage et RTTI

1. Introduction

2. static_cast

3. reinterpret_cast

4. const_cast

5. dynamic_cast

6. RTTI

5.1 Introduction

Les conversions (cast) peuvent être faites par des mots-clés dédiés

1. Casting statique (à la compilation)

static_cast<> entre types de la même famille*

reinterpret_cast<> entre types de familles différentes

const_cast<> ajouter/ supprimer const


2. Casting dynamique (à l'exécution - runtime)

dynamic_cast<> entre types de la même famille*


*Famille : les pointeurs, les références, les objets

⚠️ ATTENTION : les casts «à la C» sont possibles mais non recommandés

5.2 static_cast

Syntaxe

var = static_cast<typeDest>(var)

Exemple

char c = static_cast<char>(i);

5.2 static_cast

static_cast sert aux conversions qui auraient pu être effectuées de manière implicite



            int i = 100;
            char c = static_cast<char>(i); // int -> char
              
              float f = 100.0f;
              i = static_cast<int>(f); // float -> int
                
              class Base{};
              class Deri : public Base{};
              Der  *d = new Deri;
              Base *b = static_cast<Base*>(d); // Deri* -> Base*
          

✅ évite les warning des conversions implicites

5.2 static_cast

static_cast sert aux conversions qui auraient pu être effectuées de manière implicite



            char c = 10;       // 1 byte
            int *p = (int*)&c; // 4 bytes
            
            *p = 5; // run-time error: stack corruption
            
            int *q = static_cast<int*>(&c); // compile-time error
            

✅ détecte les erreurs à la compilation

5.3 reinterpret_cast

static_cast ne fonctionne pas avec des pointeurs de type différents


            float *pf=&f;
            double *pd=&d;
            pd = static_cast<double*> (pf); // ⛔ error
            pd = reinterpret_cas<double*>v(pf); // ✅ ok
          

reinterpret_cast sert aux conversions* entre types de familles différentes (p.ex. entier vers pointeur)


*aucune donnée n'est physiquement modifiée, ce n'est qu'une indication pour le compilateur

5.3 reinterpret_cast


            int i;
            char *ptr = reinterpret_cast<char*>(i); // int -> char*
            char &ref = reinterpret_cast<char&>(i); // int -> char&
            float *pf = reinterpret_cast<float*>(pd); // double* -> float*
          

reinterpret_cast est peu portable. Son usage est peu recommandé !


Utile pour scanner de la mémoire (scanner un range de int avec reinterpret_cast<char*>(adr) pour voir byte par byte le codage, faire un désassembleur, déc)

5.4 const_cast

const_cast permet d'ajouter ou supprimer les qualificatifs const, volatile


            class A{};
            const A *cptr;
            A *ptr = const_cast<A*>(cptr); // const A* -> A*
            
            A a;
            const A &cref = a;
            A &ref = const_cast<A&>(cref); // const A& -> A&
            

Opérateur peu utile, car :

- la conversion non-const ⇒ const est implicite

- la conversion const ⇒ non-const relève souvent d'une erreur de conception

- le qualificatif volatile est rarement utilisé

5.4 const_cast

const_cast est dangereux et peut entraîner des comportements indéfinis

Il est utilisé pour supprimer la constance des références et des pointeurs qui se réfèrent à quelque chose qui, à la base, n'est pas const

Les seules possibilités de conversion sont

X ⇔ const X

X ⇔ volatile X

volatile X ⇔ const X


volatile : sert lors de la programmation système. Elle indique qu'une variable peut être modifiée en arrière-plan par un autre programme (par exemple par une interruption, par un thread, par un autre processus, par le système d'exploitation ou par un autre processeur dans une machine parallèle)

5.5 dynamic_cast

dynamic_cast est utilisé pour les conversions entre classes polymorphiques

⇒ elle doit comporter au moins une fonction membre virtuelle

⇒ être manipulée au moyen d'un pointeur ou d'une référence


5.5 dynamic_cast

Ce type de cast est dynamique (effectué à l'exécution), et est susceptible d'échouer :

⇒ renvoie nullptr pour les conversions de pointeurs

⇒ lève une exception std::bad_cast pour les conversions de référence

5.5 dynamic_cast - Exemples


            Product *products[100];
            
            for (int i = 0; i < 100; i++)
            {
              Liquor *ptrLiquor = dynamic_cast<Liquor*>(products[i]);
                
              if (nullptr != ptrLiquor)
                cout << ptrLiquor->degree() << endl;
              
              else
                cout << products[i]->name();
            }
          

5.5 dynamic_cast - Commentaires

dynamic_cast identifie à l'exécution le type réel de l'expression reçue par RTTI -RunTime Type Information-


dynamic_cast peut facilement être utilisé à tort, signe d'une mauvaise conception :

- La fonction que l'on écrit ne travaille en fait pas sur la classe de base, mais seulement sur certaines classes dérivées bien identifiées

- Polymorphisme dynamique pas exploité (fonctions virtuelles)

- Polymorphisme statique pas exploité (utilisation de templates, surcharges)

- Hiérarchie de classes mal structurée

5.6 RTTI

RTTI - RunTime Type Information

Permet de déterminer le type d'une variable à l'exécution (runtime)

Le mécanisme RTTI contient :

- Le mot-clé de conversion dynamic_cast

- L'opérateur typeid de la classe type_info


⚠️ Note : avec certains compilateurs, le support RTTI est une option à activer

5.6 Opérateur typeid

typeid retourne un objet de type type_info qui contient des informations sur le type de l'expression

⇒ utilisé pour vérifier le type d'un objet à l'exécution



            typeid(int).name() // ⇒ i
            typeid(float).name() // ⇒ f
            typeid(2+2.00).name() // ⇒ d
            typeid(ptri).name() // ⇒ Pi
            typeid(ptrf).name() // ⇒ Pf
            typeid(refd).name() // ⇒ d
            AAA a;
            typeid(a).name() // ⇒ Z4mainE3AAA
          

5.6 Opérateur typeid - Exemple


          class Animal { public: virtual ~Animal(){}};
          class Mammifere : public Animal {};
          class Chien : public Mammifere {};
          class Caniche : public Chien {};
          
          int main() {
            Animal *ptr = new Caniche;
            typeid(ptr).name(); // ⇒ P6Animal
            typeid(*ptr).name(); // ⇒ 7Caniche
            typeid(*ptr) == typeid(Chien); // ⇒ Faux
            typeid(*ptr) == typeid(Caniche); // ⇒ Vrai

            return 0;
          }
        

5.6 Opérateur typeid vs dynamic_cast

typeid est utilisé pour vérifier le type d'un objet à l'exécution

dynamic_cast est utilisé pour convertir un pointeur ou une référence d'une classe de base en une classe dérivée


typeid est plus rapide que dynamic_cast

dynamic_cast est plus sûr que typeid


          if (typeid(*ptr) == typeid(Chien)) {
            Chien *c = dynamic_cast<Chien*>(ptr);
            if (c) {
              // ptr est un Chien
            }
          }