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 :

list et methodes virtuelles


Sujet :

Langage C++

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2009
    Messages
    162
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2009
    Messages : 162
    Points : 301
    Points
    301
    Par défaut list et methodes virtuelles
    Salut,

    le probleme que je soulève est surement bateau mais je ne le comprends pas.
    J'ai la hiérarchie de classe suivante:
    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
     
    class Widget {
    public:
        Widget() {}
     
        virtual ~Widget() {}
     
        virtual void draw() = 0;
    };
     
    class Button : public Widget {
    public:
     
        //! Constructs a new button.
        Button() : Widget() {
     
        }
     
        ~Button() { }
     
        //! Draw the widget on screen
        void draw();
     
        virtual void handleFocusLost();
     
     
    };
     
    class ToggleButton : public Button{
    public:
        //! Constructs a new button.
        ToggleAction(int x);
     
        virtual void handleFocusLost();
    };
    Ce sont des widgets pour une interface graphique. Il y a des boutons et des boutons particuliers, les ToggleButton.

    Par ailleurs j'ai une classe Menu qui affiche tous ses widgets et qui maintient donc une liste de widget, déclarée ainsi dans la classe:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    std::list<Button> actions_;
    Finalement, dans une méthode de la classe Menu, je parcours les widgets pour en appeler la méthode handleFocusLost() ainsi:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    for (std::list < Button>::iterator it = actions_.begin();
             it != actions_.end(); it++) {
            Button & b = *it;
     
            b.handleFocusLost();
        }
    Mais a l'exécution (en mettant une trace spécifique a chaque implémentation de méthode), le code précédent appelle systématiquement l'implémentation de la classe Button même si j'ai à faire à une instance de ToggleButton.
    Pourquoi est-ce le cas? La méthode handleFocusLost() est pourtant déclarée virtuelle.
    J'ai remarqué que j'ai un bon fonctionnement lorsque j'utilise des pointeurs tel que:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::list<Button*> actions_;
    Quelqu'un a t-il l'explication?

  2. #2
    Membre régulier

    Profil pro
    Inscrit en
    Décembre 2003
    Messages
    133
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2003
    Messages : 133
    Points : 113
    Points
    113
    Par défaut
    Ca donne quoi si tu remplaces

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
            Button & b = *it;
            b.handleFocusLost();
    Par

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
            (*it).handleFocusLost();
    ?

    J'imagine que quand tu mets dans b tu perds les informations relatives à la classe fille.


    En tout cas je ne sais pas quelle est la bonne pratique, mais moi j'utilise systématiquement des pointeurs plutôt que des objets directement.
    Si t'as un pointeur tu peux le caster comme t'as envie, alors que si tu caste un objet je pense que tu peux perdre de l'information ...

    Que quelqu'un me corrige si je me trompe, je n'ai pas l'habitude de donner des conseiuls ici


    EDIT : j'essaie d'expliquer où je veux en venir : quand tu fais <Button> il stocke effectivement un Button et rien de plus. Tout ce qui dépasse est perdu. Et si tu fais <Button*> tu stockes quoiqu'il arrive une adresse, et ce qui se trouve à cette adresse peut être de type Button ou un descendant.

  3. #3
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Bonjour,

    je suis d'accord avec Michel_57: en faisant le cast (Button & b = *it; est un cast; caché, mais c'est bien un cast), tu perd la "partie" fille de l'élément.
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  4. #4
    Membre expérimenté

    Profil pro
    Inscrit en
    Juin 2006
    Messages
    1 294
    Détails du profil
    Informations personnelles :
    Localisation : Royaume-Uni

    Informations forums :
    Inscription : Juin 2006
    Messages : 1 294
    Points : 1 543
    Points
    1 543
    Par défaut
    Salut,

    C'est un problème de slicing.

    MAT.

  5. #5
    Membre habitué Avatar de nowahn
    Homme Profil pro
    Inscrit en
    Août 2008
    Messages
    84
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France

    Informations forums :
    Inscription : Août 2008
    Messages : 84
    Points : 150
    Points
    150
    Par défaut
    Salut,

    Pour préciser, c’est bien un problème de slicing, au moment du stockage du bouton dans la liste (tu ne peux rien y changer en modifiant le code du corps de ta boucle for, le slicing a eu lieu avant).

    La solution est d’utiliser :
    et même certainement mieux avec des pointeurs intelligents à la place des pointeurs simples.

  6. #6
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par nowahn Voir le message
    La solution est d’utiliser :
    Il existe bien d'autres solutions
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  7. #7
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2009
    Messages
    162
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2009
    Messages : 162
    Points : 301
    Points
    301
    Par défaut
    Merci pour les réponses.
    Je me doutais bien qu'il s'agissait d'un truc comme le slicing. D'habitude, je passe par des pointeurs aussi mais là je suis parti d'un code existant.

    @r0d, tu pourrais développer?

  8. #8
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Et bien, le problème du conteneur d'objets polymorphiques est un sujet complexe sur lequel on pourrait écrire au moins un livre. En particulier en c++.
    La solution la plus simple, et peut-être la plus intuitive, donc souvent la meilleure, est celle d'une classe mère dont on stocke des pointeurs.

    Il existe des solutions à base de typelist, plus rapide (en terme de rapidité d'exécution), mais plus limitée. Voir ici par exemple.

    Il existe aussi la solution développée par boost, boost::variant. Que je ne connais pas, donc je ne sais pas quels en sont les avantages et inconvénients. J'ai ouïe dire que c'etait un peu lent, mais bon, il faut se méfier des "ouïe dires".

    Ça fait longtemps que je ne l'ai pas vu implémenté, mais je sais qu'il est également possible de passer par un espèce d'interpreteur: tu as un objet A qui propose une interface. Tu as un ensemble d'objets T (T1, ..., Tn) qui implémentent A (qui en héritent donc). A possède un pointeur sur un objet T. Et donc au final, tu auras un tableau de A, mais les A auront des T différents. Cette technique reviens un peu, finalement, à la première solution, mais elle permet tout de même une gestion plus fine de la mémoire et peut-être un peu plus de souplesse (quoi que, avec cette histoire d'interface commune c'est pas évident).

    Je connais ces solutions-là, mais il doit certainement y en avoir d'autres.
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  9. #9
    Membre averti
    Profil pro
    Inscrit en
    Juillet 2009
    Messages
    162
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2009
    Messages : 162
    Points : 301
    Points
    301
    Par défaut
    Les autres solutions sont intéressantes mais finalement plus complexes qu'une simple liste de pointeur sur la classe mère.
    Ca va m'obliger quand même a gérer l'allocation / désallocation des éléments mais on n'a rien sans rien!

  10. #10
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Effectivement: la solution la plus simple est souvent la moins compliquée

    Je suis en train d'essayer de retrouver cette solution qui utilise un espèce d'interpréteur, mais je n'y arrive pas. Je me souviens que, grâce à cette technique, on parvenait à se dédouaner d'un pointeur polymorphe ( i.e: Mere * element = new Fille; ), mais je ne parviens pas à retrouver comment ils faisaient. C'est frustrant...
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  11. #11
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Ayé je me souviens.
    C'était quelque chose comme ceci:
    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
    45
    46
    47
    48
    #include <iostream>
    #include <vector>
    #include <string>
    #include <sstream>
     
    using namespace std;
     
    struct ValueType {
    	void PrintValue( ostream & ostr ) const { ostr << str_ << endl; }
    protected:
    	string str_;
    };
     
    struct IntValue : public ValueType {
    	IntValue( int val = 0 ) { 
    		ostringstream oss;
    		oss << val;
    		str_ = oss.str();
    	}
    };
     
    struct XValue : public ValueType {
    	XValue( const string & name = "_", int id = 0 ) { 
    		ostringstream oss;
    		oss << name << id; 
    		str_ = oss.str();
    	}
    };
     
    struct Element {
    	Element( const ValueType & val_type = IntValue() ) : ValType( val_type ) {}
     
    	ValueType ValType;
    };
     
    int main()
    {
    	vector< Element > v;
    	v.push_back( Element( IntValue( 1 ) ) );
    	v.push_back( Element( XValue( "x", 2 ) ) );
     
    	for ( vector< Element >::const_iterator it = v.begin(); it != v.end(); ++it )
    		it->ValType.PrintValue( cout );
     
    	cout << "fini" << endl;
    	cin.get();
    	return 0;
    }
    Après, quel peut être l'intérêt concret d'une telle technique? C'est une bonne question, à laquelle j'aurais du mal à répondre. En revanche, les dangers y sont nombreux.
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

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

Discussions similaires

  1. Réponses: 9
    Dernier message: 13/02/2007, 15h29
  2. Réponses: 2
    Dernier message: 20/10/2006, 15h07
  3. [debutant] probleme avec methode virtuelle pure
    Par Treuze dans le forum Débuter
    Réponses: 10
    Dernier message: 21/04/2006, 12h58
  4. Réponses: 19
    Dernier message: 10/02/2005, 22h43
  5. [Language] Méthodes virtuelle ?
    Par Eltaz dans le forum Langage
    Réponses: 2
    Dernier message: 12/01/2005, 21h43

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