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 :

downcast et liste d'instances


Sujet :

Langage C++

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    42
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Octobre 2009
    Messages : 42
    Par défaut downcast et liste d'instances
    Bonjour,

    j'ai une classe mere avec une fonction virtuelle:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    class A{
    protected:
       unsigned int m_type;
     
    public:
       A();
       virtual ~A();
       virtual bool do();
    };
    et des classes filles Aa et Ab qui implementent la methode do().

    je souhaite avoir une liste vector<A*> myList et pouvoir iterer de maniere à appeler la bonne methode do() en fonction de la fille ( *it->do() ), tout en restant générique. Or j'ai lu (et testé) que le downcast en C++ n'etait pas safe (contrairement à Java).

    Alors, je ne vois pas comment procéder. Avez-vous des conseils à me donner?

    Merci de votre aide

  2. #2
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 393
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 393
    Par défaut
    La fonction do() est virtuelle, donc tu as déjà le comportement que tu veux.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  3. #3
    Membre émérite

    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    717
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 717
    Par défaut
    En effet, aucun cast dans le code que tu montre.

    Un downcast serait :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Aa* aa = (Aa*)a; // Non safe
    Aa* aa = dynamic_cast<Aa*>(a); // Safe

  4. #4
    Membre averti
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    42
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Octobre 2009
    Messages : 42
    Par défaut manque de clarté desolée
    je m'explique donc:

    dans mon main j'instancie un Aa* pAa et un Ab* pAb, chacun ayant sont m_type à 0 et 1 respectivement (via un enum).

    Par la suite je les "push" dans la liste:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    myList.push_back(dynamic_cast<A*>(pAa));
    myList.push_back(dynamic_cast<A*>(pAb));
    Or myList[0]->do() appelle la declaration dans la classe mère et non celle de la fille.

  5. #5
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 393
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 393
    Par défaut
    Euh, mais tu n'as même pas à faire de dynamic_cast, ici...

    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 <list>
     
    class A{
     
    public:
       A() {}
       virtual ~A() {}
       virtual void do() { std::cout<< "A" << std::endl; }
    };
     
    class Aa : public A {
     
    public:
       Aa() {}
       virtual ~Aa() {}
       virtual void do() { std::cout<< "Aa" << std::endl; }
    };
     
    class Ab : public A {
     
    public:
       Ab() {}
       virtual ~Ab() {}
       virtual void do() { std::cout<< "Ab" << std::endl; }
    };
     
    void TestHeritage(void)
    {
      std::list< A* > myList;
      myList.push_back(new Aa);
      myList.push_back(new Ab);
     
      for(std::list< A* >::iterator it=myList.begin ; it!=myList.end() ; ++it)
      {
        (*it)->do();
      }
    }
    Ceci, si ça compile, devrait donner le bon comportement.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  6. #6
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 635
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    Salut,

    Pour te permettre de comprendre...

    Si tu as une hiérarchie de classes proche de
    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
    class A
    {
        public:
            virtual ~a(); //nécessaire si tu veux pouvoir détruire tes objets
                          // dérivés au départ des pointeurs sur A
            virtual bool do();
        /*tout le reste */
    };
    class B : public A
    {
        /*les trucs adaptés pour B */
    };
    class C : public A
    {
        /* les trucs adaptés pour C */
    };
    Tu dispose, du fait de l'héritage, d'une relation EST-UN pour tes classe B et C.

    En effet, B EST-UN A et on peut en dire autant de la part de C.

    De ce fait, n'importe quel B ou n'importe quel C peut parfaitement passer pour un A, bien qu'il souffre de la restriction que tu ne puisse appeler que les fonctions déclarées dans A.

    Si donc, tu crées des B et des C et que tu les places dans une collection de pointeurs de type A, tu pourra sans problème invoquer la fonction do depuis n'importe quel élément de la dite collection.

    Comme la fonction do est déclarée virtuelle, son comportement sera adapté au type dynamique de l'objet, si elle est redéfinie dans une des classes enfant de A.

    C'est ca ce que l'on appelle le polymorphisme (enfin, l'un des polymorphismes possibles en C++)

    Il n'y aura que si tu veux, en partant de ta collection de pointeurs sur des objets de type A, pouvoir appeler des comportements spécifiques au B ou au C que tu devra envisager le downcasting.

    Dans ce cas, il vaut mieux privilégier un transtypage "sécurisant" tel que dynamic_cast ou static_cast par rapport à un transtypage sauvage "a la C"
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  7. #7
    Membre averti
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    42
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Octobre 2009
    Messages : 42
    Par défaut j'ai compris...
    je ne dois pas faire de cast justement!

    Exemple:
    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
    39
    40
    41
    42
    43
    44
     
    #include <string>
    #include <iostream>
    #include <vector>
    using namespace std;
     
    class Chumain{
    protected:
    	std::string m_Genre;
    public:
    	Chumain(){};
    	virtual ~Chumain(){};
    	virtual void jesuis(){cout<<"je suis un humain"<<endl;};
    };
     
    class Cfemme: public Chumain{
    public:
    	Cfemme(){};
    	virtual ~Cfemme(){};
    	void jesuis(){cout<<"je suis une femme"<<endl;};
    };
     
    class Chomme: public Chumain{
    public:
    	Chomme(){};
    	virtual ~Chomme(){};
    	void jesuis(){cout<<"je suis un homme"<<endl;};
    };
     
     
     
    int main() {
    	Cfemme* sophie = new Cfemme();
    	Chomme* vincent = new Chomme();
     
    	std::vector<Chumain*> mesHumains;
    	mesHumains.push_back(sophie);
    	mesHumains.push_back(vincent);
     
    	for (std::vector<Chumain*>::iterator it = mesHumains.begin(); it != mesHumains.end(); it++) {
    		(*it)->jesuis();
    	}
    	return 0;
    }
    au run, on a les traces suivantes:
    je suis une femme
    je suis un homme
    Merci

  8. #8
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 635
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    Citation Envoyé par zerbynette Voir le message
    je ne dois pas faire de cast justement!
    Ben oui, c'est bien ce que je t'expliquais...

    Ici, tu laisse simplement agir le polymorphisme

    il n'y a que si tu veux récupérer, selon ton exemple, un objet de type CFemme pour le premier élément de ta liste (parce que tu veux appeler, par exemple, la fonction membre tricoter, qui n'existe (soyons machos ) que pour la classe CFemme ) que tu devra recourir au transtypage
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  9. #9
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Salut,
    Pour tout savoir ou presque sur les fonctions virtuelles : Les fonctions virtuelles en C++, par votre serviteur

  10. #10
    Membre averti
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    42
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Octobre 2009
    Messages : 42
    Par défaut mais un doute subsiste
    car quand j'ecris:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    	Chumain* humain = new Chumain();
    	Cfemme* annie = static_cast<Cfemme*> (humain);
    	annie->jesuis();
    la trace retournée est : je suis un humain... Le downcast n'a pas marché

  11. #11
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Le downcast ne transforme pas une variable de type Chumain en variable de type Cfemme. Il dit que tu connais le type dynamique à la compilation et c'est pour ça que tu veux obtenir un pointeur dessus.

    Le type dynamique d'une variable ne change pas en cours d'exécution :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Chumain* humain = new Chumain();
    Tu alloues une variable de type dynamique Chumain et humain pointe vers cette variable.
    Lorsque tu écris :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Cfemme* annie = static_cast<Cfemme*> (humain);
    Tu dis au compilateur que la variable humain a un type dynamique Cfemme et que donc tu veux l'utiliser avec annie . Or c'est faux.
    Tu as juste de la chance que ça ne plante pas. Je n'ai pas vérifié, mais je pense qu'il s'agit d'un comportement indéterminé.

  12. #12
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 393
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 393
    Par défaut
    Dans ces circonstances, un dynamic_cast<> aurait gueulé (retourné NULL ou lancé une exception std::bad_cast selon que tu aies employé un pointeur ou une référence)
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  13. #13
    Membre averti
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    42
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Octobre 2009
    Messages : 42
    Par défaut comment faire le downcast alors?
    car oui, avec dynamic_cast, la compilation passe mais l'execution plante.
    Comment faire alors?

  14. #14
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 393
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 393
    Par défaut
    Eh bien, ne pas tenter ce downcast?
    Dans un modèle objet parfait, tu n'es pas censée en avoir besoin.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  15. #15
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    	Chumain* humain = new Cfemme();
    	Cfemme* annie = static_cast<Cfemme*> (humain);
    	annie->jesuis();
    Tu ne peux en aucun cas changer le type dynamique d'une variable.
    static_cast fait un cast à la compilation avec le risque que ce soit faux à l'exécution. Ce qui est ton cas.
    dynamic_cast permet de vérifier à l'exécution que le type dynamique est compatible :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    	Chumain* humain = new Chumain();
    	Cfemme* annie = dynamic_cast<Cfemme*> (humain);
            if(annie){
    		annie->jesuis();
    	}
    	else{
    		std::cout<<"humain n'est pas Cfemme \n !";
    	}
    Note que dans la plus part des cas, c'est une erreur de conception que de vouloir faire un down cast (dynamic_cast ou static_cast).

  16. #16
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 393
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 393
    Par défaut
    D'ailleurs, Chumain devrait sans doute être une classe abstraite.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  17. #17
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 635
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    Il faut comprendre que, si tu peux faire passer une instance de la classe fille pour une instance de la classe mère, tu ne peux pas faire le contraire...

    La raison principale en est que la classe fille va (vraisemblablement) utiliser plus d'espace mémoire que la classe mère, et que si classe fille connait tout de la classe mère (du moins, tout ce qui est publique et protégé), la classe mère ne sait absolument rien de la class fille.

    Donc, si tu as besoin d'un objet de la classe fille, tu crées un objet de... la classe fille...

    Soit statiquement
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    Fille lafille(/* paramètre éventuel */);
    /*peut être transmise à la fonction */
    void foo(Mere const & m)
    {
        /*...*/
    }
    soit dynamiquement:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    /* en sachant qu'il s'agit d'un pointeur sur une instance de la classe Fille */
    Fille  * ptrfille= new Fille(/* paramètre éventuel*/);
    /* ou en faisant passer le pointeur comme un pointeur sur une instance
     * de la classe Mere 
     */
    Mere * ptr = new Fille(/*parametre éventuel */);
    Le premier peut être passé en paramètre à une fonction attendant un pointeur ou une référence vers une instance de la classe Fille ou de la classe Mere, le second ne peut l'être qu'au travèrs du transtypage.

    Si tu veux surpasser cette restriction, tu dois t'orienter vers le pattern décorateur
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  18. #18
    Membre averti
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    42
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Octobre 2009
    Messages : 42
    Par défaut Merci à tous
    pour vos réponses et vos conseils.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Liste des instances d'une classe
    Par alexdevl dans le forum Général Python
    Réponses: 32
    Dernier message: 14/09/2011, 22h52
  2. Trier une liste d'instance avec sort()
    Par Yachas dans le forum Général Python
    Réponses: 7
    Dernier message: 10/02/2011, 03h31
  3. Liste les instances d'une classe
    Par Mucho dans le forum Langage
    Réponses: 2
    Dernier message: 12/09/2009, 17h24
  4. STL Problème avec une liste d'instances de class
    Par BruceBoc dans le forum SL & STL
    Réponses: 12
    Dernier message: 16/02/2007, 14h12
  5. liste des instances
    Par exyacc dans le forum MS SQL Server
    Réponses: 2
    Dernier message: 30/06/2006, 11h18

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