IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
logo

FAQ C++Consultez toutes les FAQ

Nombre d'auteurs : 34, nombre de questions : 368, dernière mise à jour : 14 novembre 2021  Ajouter une question

 

Cette FAQ a été réalisée à partir des questions fréquemment posées sur les forums de http://www.developpez.com et de l'expérience personnelle des auteurs.

Je tiens à souligner que cette FAQ ne garantit en aucun cas que les informations qu'elle propose sont correctes ; les auteurs font le maximum, mais l'erreur est humaine. Cette FAQ ne prétend pas non plus être complète. Si vous trouvez une erreur ou si vous souhaitez devenir rédacteur, lisez ceci.

Sur ce, nous vous souhaitons une bonne lecture.

SommaireLe langage C++Les conversions de types (3)
précédent sommaire suivant
 

Contrairement au C, les cast C++ sont effectués au moyen d'opérateurs spécifiques (mots-clés réservés). Le choix de ces opérateurs dépend du type de cast, car le C++ différencie quatre types de conversion explicite. Tout d'abord, trois opérateurs sont dédiés aux conversions statiques (effectuées à la compilation) :

  1. static_cast : entre types de même famille. Il s'agit la plupart du temps d'expliciter des conversions qui auraient pu être effectuées de manière implicite, mais souvent avec un avertissement du compilateur.

    Code c++ : Sélectionner tout
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // conversion de int en char 
    int i = 100; 
    char c = static_cast<char>( i ); 
      
    // conversion de float en int 
    float f = 100.0f; 
    i = static_cast<int>( f ); 
      
    // conversion classes dérivée -> classe parent 
    class A {}; 
    class B : public A {}; 
      
    B *b = new B; 
    A *a = static_cast<A*>( b );
  2. reinterpret_cast : entre types de familles différentes. Il s'agit simplement de dire au compilateur « je sais que je manipule une donnée de type X, mais on va faire comme si elle était de type Y ». De ce fait aucune donnée n'est physiquement modifiée, cet opérateur de conversion n'est qu'une indication pour le compilateur.

    Code c++ : Sélectionner tout
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // Conversion de int en pointeur de char 
    int i; 
    char * ptr = reinterpret_cast<char*>( i ); 
      
    // Conversion de int en référence sur char 
    char & ref = reinterpret_cast<char&>( i ); 
      
    // Conversion pointeur de char -> pointeur de double 
    double * ptr2 = reinterpret_cast<double*>( ptr );
  3. const_cast: entre un type donné constant et le même type avec / sans les qualificateurs const ou volatile. Cet opérateur est rarement utilisé, car :
    • La conversion non-const -> const est implicite.
    • La conversion const -> non-const relève en fait bien souvent d'une erreur de conception.
    • Le qualificateur volatile est rarement utilisé.


    Code c++ : Sélectionner tout
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class A {}; 
      
    // Conversion de const A * en A * 
    const A * cptr; 
    A * ptr = const_cast<A*>( cptr ); 
      
    // Conversion de const A & en A & 
    A a; 
    const A & cref = a; 
    A & ref = const_cast<A&>( cref );

Le C++ supporte aussi un autre type de conversion : depuis une classe parent vers une classe enfant (downcasting) ou depuis une classe parent vers une autre classe parent (crosscasting). La classe parent doit être à usage polymorphe, autrement dit elle doit obligatoirement comporter au moins une fonction membre virtuelle, et être manipulée au moyen d'un pointeur ou d'une référence (la virtualité implique l'utilisation de pointeurs ou de références, voir Que signifie le mot-clé virtual ?). Ce type de cast étant dynamique (effectué à l'exécution), il est susceptible d'échouer et de lever une exception std::bad_cast dans le cas d'une conversion de références, ou de renvoyer NULL dans le cas d'une conversion de pointeurs.

L'opérateur associé à ce cast est dynamic_cast :

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <iostream> 
#include <string> 
#include <typeinfo> 
  
class A 
{ 
public: 
    virtual std::string get_type() = 0; 
}; 
  
class B : public A 
{ 
public: 
    virtual std::string get_type() { return "B"; } 
}; 
  
class C : public A 
{ 
public: 
    virtual std::string get_type() { return "C"; }; 
}; 
  
A * create_B_or_C() 
{ 
    static int nb = 0; 
    // si nb est pair, on crée un B, sinon un C 
    ++nb; 
    if ( nb % 2 == 0 ) { return new B; } 
    return new C; 
} 
  
int main() 
{ 
    for ( int i = 0; i < 5; ++i ) 
    { 
        A *a = create_B_or_C(); 
        std::cout << "Test sur un " << a->get_type() << " : "; 
  
        B *b = dynamic_cast<B*>( a ); 
        if ( b == 0 ) 
        { 
            // échec du cast en B, il doit s'agir d'un C 
            // lève std::bad_cast s'il ne s'agit pas d'un C 
            try 
            { 
                C & c = dynamic_cast<C&>( *a ); 
                std::cout << "il s'agit d'un C.\n"; 
            } 
            catch ( const std::bad_cast & ) 
            { 
                std::cout << "Oups!\n"; 
            } 
        } 
        else 
        { 
            std::cout << "il s'agit d'un B.\n"; 
        } 
        delete a; 
    } 
}
dynamic_cast identifie à l'exécution le type réel de l'expression reçue au moyen des informations de type à l'exécution (RTTI). Cette fonctionnalité doit donc être activée dans votre compilateur pour pouvoir utiliser dynamic_cast, ce qui n'est pas le cas par défaut avec certains compilateurs (tel que VC++, voir l'option /GR).

Dernière remarque concernant dynamic_cast : celui-ci est souvent utilisé à tort, surtout par les débutants. Voir Pourquoi l'utilisation du downcasting est-il souvent une pratique à éviter ?. Voir également Qu'est-ce que le cross-casting ?.

Si vous souhaitez tester votre maîtrise des casts C++, nous vous renvoyons à l'item n° 17 des GOTW (Guru Of The Week)
( http://www.gotw.ca/gotw/017.htm).

Mis à jour le 17 mars 2008 Aurelien.Regat-Barrel Laurent Gomila

On est souvent tenté d'utiliser l'opérateur de conversion dynamic_cast pour effectuer un downcasting (voir Comment effectuer une conversion de type explicite (cast) ?), mais il s'agit fréquemment d'une erreur. Pourquoi ? Car conceptuellement parlant, on a en réalité rarement besoin de connaître le type réel d'un objet que l'on manipule via sa classe de base. Si l'on se retrouve dans une telle situation, c'est probablement que :

  • 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.
  • On n'a pas exploité un éventuel polymorphisme dynamique (utilisation des fonctions virtuelles, voir Que signifie le mot-clé virtual ?).
  • On n'a pas exploité un éventuel polymorphisme statique (utilisation des templates et des surcharges).
  • On a mal conçu notre hiérarchie.

Le downcasting est donc à utiliser avec parcimonie, lorsque l'on n'a pas le choix ou que l'on sait exactement ce que l'on fait. Par exemple dans le cas des plugin, ou encore lorsque l'on travaille avec un code, une bibliothèque ou une API qui ne connaîtrait (et donc ne pourrait manipuler) que la classe de base d'une hiérarchie. Les classes dérivées étant écrites par le client, le downcasting est donc une solution simple pour communiquer avec la bibliothèque en question.
On peut également citer des implémentations de double-dispatching utilisant le downcasting (notamment dans la bibliothèque Loki).

Il faut donc toujours s'interroger sur l'utilisation que l'on fait de dynamic_cast : est-ce une nécessité, ou seulement un moyen pratique de contourner une erreur de conception ?

Mis à jour le 17 octobre 2005 Aurelien.Regat-Barrel Laurent Gomila

Le cross-casting est une possibilité offerte par le C++ grâce à son support de l'héritage multiple. Dans l'exemple suivant :

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// A   B 
//  \ / 
//   C 
  
class A 
{ 
public: 
    virtual ~A(); 
}; 
  
class B 
{ 
public: 
    virtual ~B(); 
}; 
  
class C : public A, public B 
{ 
};
un cast classique permet, à partir de C de convertir en A ou en B. Le cross casting consiste par exemple à obtenir une instance de B à partir d'une instance de... A !

Code c++ : Sélectionner tout
1
2
A *a = new C; 
B *b = dynamic_cast<B*>( a );
cet exemple fonctionne car il revient à faire :

Code c++ : Sélectionner tout
1
2
3
A *a = new C; 
C *c = dynamic_cast<C*>( a ); // downcasting de A en C 
B *b = static_cast<B*>( c );  // cast de C en B
Cette décomposition illustre pourquoi le recours à dynamic_cast est obligatoire (pour le downcasting). Nous avons donc fait une conversion au travers de la hiérarchie d'héritage, d'où ce terme de cross casting.

Pour plus de précisions sur le cross-casting et ses applications, vous pouvez lire ce document : Cross Casting: The Capsule Pattern.

Mis à jour le 17 octobre 2005 Aurelien.Regat-Barrel

Proposer une nouvelle réponse sur la FAQ

Ce n'est pas l'endroit pour poser des questions, allez plutôt sur le forum de la rubrique pour ça


Réponse à la question

Liens sous la question
précédent sommaire suivant
 

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2024 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.