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

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les r�ponses en temps r�el, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

static_pointer_cast et dynamic_pointer_cast


Sujet :

Langage C++

  1. #1
    Membre averti Avatar de CaptainKrabs
    Homme Profil pro
    �tudiant
    Inscrit en
    Juin 2018
    Messages
    25
    D�tails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rh�ne (Rh�ne Alpes)

    Informations professionnelles :
    Activit� : �tudiant

    Informations forums :
    Inscription : Juin 2018
    Messages : 25
    Par d�faut static_pointer_cast et dynamic_pointer_cast
    Bonjour,

    je cherche � comprendre les static_pointer_cast et les dynamic_pointer cast. On m'a indiqu� que ces templates permettent de faire des cast de shared_ptr.
    Cependant en relisant l'extrait de code suivant, j'avoue avoir du mal � comprendre :

    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    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
     
     
    #include <iostream>
    #include <memory>
     
    using namespace std;
     
    class Polygone{
    public:
        virtual void display(){cout << "Polygone" << endl;}
     
    };
     
    class Triangle : public Polygone{
    public:
        virtual void display(){cout << "Triangle display" << endl;}
        virtual void display2(){cout << "Triangle display2" << endl;}
     
    };
     
    int main(int argc, char* argv[]){
        shared_ptr<Polygone> l_sharedPtrPoly(new Polygone);
        l_sharedPtrPoly->display();
     
        shared_ptr<Triangle> l_sharedPtrTriSt = static_pointer_cast<Triangle>(l_sharedPtrPoly);
        l_sharedPtrTriSt->display();
     
        // Downcast d'un objet polymorphique avec dynamic_pointer_cast
        shared_ptr<Triangle> l_sharedPtrTriDyn = dynamic_pointer_cast<Triangle>(l_sharedPtrPoly);
        if(l_sharedPtrTriDyn){
            l_sharedPtrTriDyn->display();
            l_sharedPtrTriDyn->display2();
        }else{
            cout << "Pointeur nul" << endl;
        }
     
        return 0;
    }
    J'obtiens alors en sortie :
    Polygone
    Polygone
    Pointeur nul

    Voici ce qui me questionne :
    1) Je m'attendrais � obtenir en sortie Triangle sur la 2�me ligne. L�, je ne comprends pas l'int�r�t du cast.
    2) Je ne comprends pas la 3eme ligne. C'est comme si le cast n'avait pas fonctionn� et le pointeur est non initialis�. Est-ce que cela est d� qu'ici mes objets ne sont pas dynamiques?

    Merci par avance de vos r�ponses.

  2. #2
    Membre Expert
    Homme Profil pro
    Ing�nieur d�veloppement logiciels
    Inscrit en
    Juin 2011
    Messages
    767
    D�tails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, H�rault (Languedoc Roussillon)

    Informations professionnelles :
    Activit� : Ing�nieur d�veloppement logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 767
    Par d�faut
    Les xxx_pointer_cast suivent les m�mes r�gles que les xxx_cast et il semble y avoir un probl�me de compr�hension sur les casts en g�n�ral. Un cast d'une r�f�rence (pointeur comprit) ne change pas le type r�el de la donn�e, il indique que maintenant le type doit �tre trait� comme un autre type, avec tout les cons�quences que cela implique en cas d'erreur.

    Comme le type construit n'est pas un Triangle, il n'y a pas sp�cialement de raison que Triangle s'affiche, la vtable est toujours celle de Polygone. Mais bien pire que cela, ce code � un comportement ind�fini puisque downcaster un pointeur vers un type qu'il n'est pas est UB.

    Quand a dynamic_cast, qui fait une v�rification du type demand�, il donne � raison un pointeur nul: l_sharedPtrPoly ne contient pas un Triangle.

    Ce genre de fonction est vraiment � prendre avec des pincettes. Et mon avis que dynamic_cast met surtout en �vidence une erreur de conception.

  3. #3
    Expert confirm�
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    4 266
    D�tails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activit� : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 4 266
    Par d�faut
    Hello,

    Soyons clairs : le C++ ne pardonne pas l'approximation. Il exige une rigueur absolue. Manquer de cette rigueur m�ne in�vitablement � des comportements ind�finis et � des erreurs difficiles � d�boguer.

    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    shared_ptr<Polygone> l_sharedPtrPoly(new Polygone);
    Ici, vous allouez dynamiquement un objet de type Polygone sur le tas. l_sharedPtrPoly est un shared_ptr qui g�re la dur�e de vie de cet objet Polygone. Point crucial : l'objet r�el, celui qui existe en m�moire, est un Polygone. Ce fait est immuable, quels que soient les casts que vous tenterez par la suite. Un cast ne transforme pas un objet d'un type en un autre ; il change seulement la mani�re dont le compilateur interpr�te le type du pointeur ou de la r�f�rence qui pointe vers cet objet.

    Votre classe Polygone poss�de une fonction virtuelle :

    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    virtual void display(){cout << "Polygone" << endl;}
    Lorsqu'une classe contient au moins une fonction virtuelle, le compilateur met en place un m�canisme appel� la "table des fonctions virtuelles" (vtable). Chaque objet d'une classe polymorphique (comme Polygone et Triangle) contient un pointeur cach�, souvent appel� vptr (virtual pointer), qui pointe vers la vtable de sa classe.


    • Vtable de Polygone : Elle contiendra une entr�e pointant vers l'impl�mentation de Polygone::display.
    • Vtable de Triangle : Elle contiendra une entr�e pointant vers l'impl�mentation de Triangle::display (puisqu'elle surcharge display).



    Lorsque vous appelez une fonction virtuelle via un pointeur ou une r�f�rence de type de base, comme ici :


    Le programme, � l'ex�cution, suit ces �tapes :

    1. Acc�de � l'objet point� par l_sharedPtrPoly (qui est un Polygone).
    2. Utilise le vptr de cet objet pour trouver la vtable de Polygone.
    3. Dans cette vtable, il trouve l'adresse de la fonction display appropri�e pour un Polygone.
    4. Ex�cute Polygone::display().



    C'est ce qu'on appelle la "liaison tardive" ou "dynamic dispatch", et c'est le c�ur du polymorphisme en C++. C'est pourquoi votre premi�re sortie est "Polygone".

    Vous vous demandez si le probl�me vient du fait que "vos objets ne sont pas dynamiques". C'est une erreur d'interpr�tation. Vos objets sont allou�s dynamiquement avec new. Le probl�me n'est pas la m�thode d'allocation, mais le type r�el de l'objet allou�. Que l'objet soit sur la pile (statique/automatique) ou sur le tas (dynamique) ne change pas fondamentalement le fonctionnement des casts de type dans ce contexte, si ce n'est que les shared_ptr sont typiquement utilis�s pour la gestion de la m�moire dynamique.

    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    3
    shared_ptr<Triangle> l_sharedPtrTriSt = static_pointer_cast<Triangle>(l_sharedPtrPoly);
     
    l_sharedPtrTriSt->display();
    Vous vous attendez � "Triangle" en sortie, mais vous obtenez "Polygone". Pourquoi?

    static_pointer_cast (et son �quivalent pour les pointeurs bruts, static_cast) effectue une conversion de type bas�e sur les informations disponibles � la compilation. Il ne fait aucune v�rification � l'ex�cution pour s'assurer que la conversion est r�ellement valide. Lorsque vous faites un "downcast" (conversion d'un pointeur de classe de base vers un pointeur de classe d�riv�e), static_cast suppose que vous, le d�veloppeur, savez ce que vous faites et que l'objet point� est effectivement du type cible (ou d'un type qui en d�rive).
    Dans votre cas, l_sharedPtrPoly (de type shared_ptr<Polygone>) pointe vers un objet qui est un Polygone. Vous demandez au compilateur : "Traite ce pointeur comme s'il pointait vers un Triangle". Le compilateur ob�it aveugl�ment. l_sharedPtrTriSt devient un shared_ptr<Triangle>, mais il pointe toujours vers le m�me objet Polygone en m�moire.


    Lorsque vous appelez l_sharedPtrTriSt->display(), le m�canisme des fonctions virtuelles entre � nouveau en jeu :

    1. L'objet sous-jacent est toujours le Polygone original.
    2. Son vptr pointe toujours vers la vtable de Polygone.
    3. L'appel � display() est r�solu via cette vtable, ce qui ex�cute Polygone::display().



    Voil� pourquoi vous obtenez "Polygone" et non "Triangle". Le static_pointer_cast a chang� le type statique du pointeur (la fa�on dont le compilateur le voit), mais pas le type dynamique de l'objet (ce qu'il est r�ellement). L'appel virtuel se base sur le type dynamique.

    L'utilisation de static_pointer_cast pour un downcast est dangereuse si l'objet n'est pas r�ellement du type cible. Si vous aviez tent� d'appeler une m�thode sp�cifique � Triangle qui n'existe pas dans Polygone, comme display2():

    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    // l_sharedPtrTriSt->display2(); // ATTENTION!
    Vous auriez invoqu� un comportement ind�fini (UB). L'objet Polygone ne poss�de pas de m�thode display2(). Tenter d'y acc�der via un pointeur de type Triangle* (obtenu par un static_pointer_cast incorrect) peut mener � un crash, � des r�sultats incorrects, ou � tout autre comportement impr�visible. Le compilateur ne vous prot�ge pas ici ; il vous fait confiance.Le static_cast est une affirmation de votre part que le cast est valide. Si cette affirmation est fausse, les cons�quences sont pour vous. C'est un outil puissant, mais qui requiert une certitude absolue sur les types manipul�s.

    Passons � votre deuxi�me interrogation.

    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    3
    4
    5
    6
    7
    shared_ptr<Triangle> l_sharedPtrTriDyn = dynamic_pointer_cast<Triangle>(l_sharedPtrPoly);
     
    if(l_sharedPtrTriDyn){
        //...
    }else{
        cout << "Pointeur nul" << endl;
    }
    Vous obtenez "Pointeur nul", et vous ne comprenez pas pourquoi, suspectant que cela est d� au fait que "vos objets ne sont pas dynamiques".

    dynamic_pointer_cast (et son �quivalent dynamic_cast pour les pointeurs bruts) est con�u pour effectuer des conversions de type de mani�re s�re au sein d'une hi�rarchie de classes polymorphiques (c'est-�-dire, des classes ayant au moins une fonction virtuelle). Contrairement � static_cast, dynamic_cast effectue une v�rification � l'ex�cution pour s'assurer de la validit� du cast. Cette v�rification s'appuie sur les informations de type � l'ex�cution (Run-Time Type Information - RTTI).

    Pour un downcast (de base vers d�riv�e) :

    • Si l'objet point� par le pointeur de base est effectivement une instance de la classe d�riv�e cible (ou d'une classe qui en h�rite), dynamic_cast r�ussit et retourne un pointeur du type d�riv� pointant vers l'objet.
    • Si l'objet point� n'est pas une instance de la classe d�riv�e cible, dynamic_cast �choue. Pour les pointeurs, il retourne nullptr. Pour les r�f�rences, il l�verait une exception std::bad_cast.


    Pourquoi "Pointeur Nul"?

    Dans votre code :

    1. l_sharedPtrPoly pointe vers un objet Polygone.
    2. Vous tentez de le caster en shared_ptr<Triangle> avec dynamic_pointer_cast.
    3. � l'ex�cution, dynamic_pointer_cast examine l'objet r�el. Il constate que cet objet est un Polygone, et non un Triangle.
    4. Le cast est donc invalide.
    5. Par cons�quent, dynamic_pointer_cast retourne un shared_ptr nul (un shared_ptr qui ne g�re aucun objet).



    Votre if(l_sharedPtrTriDyn) �value donc � false, et le bloc else est ex�cut�, affichant "Pointeur nul". C'est le comportement attendu et correct de dynamic_pointer_cast dans cette situation. Il vous signale que le cast n'est pas s�r et a �chou�.La condition pour que dynamic_cast (et donc dynamic_pointer_cast) fonctionne pour les downcasts et crosscasts est que la classe de base soit polymorphique (c'est-�-dire qu'elle ait au moins une fonction virtuelle). Votre classe Polygone l'est, gr�ce � virtual void display().

    L'�chec n'est pas d� au fait que les objets "ne sont pas dynamiques" (ils le sont), mais au fait que l'objet point� par l_sharedPtrPoly n'est pas, et n'a jamais �t�, un Triangle.

    Pour ajouter,


    Bien que ce ne soit pas directement li� � vos questions, il est vital de noter une omission dans votre code original qui peut conduire � des probl�mes graves, en particulier avec les shared_ptr et l'h�ritage : l'absence de destructeur virtuel dans la classe de base Polygone.
    Si vous g�rez un objet de classe d�riv�e (Triangle) via un pointeur de classe de base (Polygone* ou shared_ptr<Polygone>), et que la classe de base n'a pas de destructeur virtuel, alors la suppression de l'objet via le pointeur de base n'appellera que le destructeur de la classe de base. Le destructeur de la classe d�riv�e ne sera pas appel�, ce qui peut entra�ner des fuites de ressources si la classe d�riv�e allouait des ressources dans son constructeur.








    Bonne pratique : Toute classe de base destin�e � �tre utilis�e polymorphiquement (c'est-�-dire, avoir des classes d�riv�es et �tre manipul�e via des pointeurs/r�f�rences de base) devrait d�clarer un destructeur virtuel.

    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    3
    4
    5
    class Polygone {
    public:
        virtual void display() { /*... */ }
        virtual ~Polygone() { /*... */ } // BONNE PRATIQUE!
    };


    M�me si le destructeur ne fait rien, sa simple d�claration en tant que virtual assure que la cha�ne de destruction correcte sera appel�e. std::shared_ptr g�re cela correctement si le destructeur est virtuel.


    Le choix du bon cast n'est pas une question de pr�f�rence stylistique, mais une d�cision technique critique bas�e sur les garanties que vous poss�dez sur les types des objets en jeu. En cas de doute, la s�curit� offerte par dynamic_pointer_cast doit toujours primer.
    La ma�trise des casts, du polymorphisme et de la gestion de la m�moire est un jalon essentiel pour tout d�veloppeur C++ qui se respecte. Ce ne sont pas de simples fonctionnalit�s du langage ; ce sont les fondations de paradigmes de conception qui, lorsqu'ils sont bien compris et appliqu�s, m�nent � du code flexible, maintenable, robuste et performant. Mal compris ou mal utilis�s, ils m�nent in�luctablement � la fragilit� et au chaos. Soyez rigoureux.




  4. #4
    Membre averti Avatar de CaptainKrabs
    Homme Profil pro
    �tudiant
    Inscrit en
    Juin 2018
    Messages
    25
    D�tails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rh�ne (Rh�ne Alpes)

    Informations professionnelles :
    Activit� : �tudiant

    Informations forums :
    Inscription : Juin 2018
    Messages : 25
    Par d�faut
    Bonjour,

    Merci beaucoup pour vos r�ponses. Elles sont tr�s compl�tes et c'est beaucoup plus claire pour moi � pr�sent.

    Bien � vous.

+ R�pondre � la discussion
Cette discussion est r�solue.

Discussions similaires

  1. Pb de compilation avec dynamic_pointer_cast
    Par vandamme dans le forum Boost
    R�ponses: 1
    Dernier message: 15/09/2009, 12h30

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo