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

C++ Discussion :

Héritage et tableau d'objets


Sujet :

C++

  1. #1
    Membre éclairé
    Avatar de Mindiell
    Profil pro
    Inscrit en
    Juin 2006
    Messages
    735
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 735
    Par défaut Héritage et tableau d'objets
    Bnojour à tou(te)s,

    Suite à mon post de ce matin (Héritage et constructeur par copie) j'ai un nouveau souci
    J'ai maintenant un tableau qui contient tout un tas d'objets qui héritent tous d'une même classe ou d'une sous-classe, etc...

    J'ai créé une fonction GetElementByName afin de récupérer un élément de mon tableau. Le problème (encore) c'est qu'il me récupère un cBase* (cBase est ma classe de base donc) et n'a donc pas connaissance des méthodes des classes dérivées :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    cBase * GetElementByName (std::string Name)
    {
    // Création d'un itérateur sur mon tableau, donc un itérateur de cBase*
    // Recherche
    // Retour d'un cBase* donc...
    }
    Comment je fais pour pouvoir retourner la bonne classe ?

    Merci encore pour votre patience !

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 633
    Par défaut
    Si tu dois retrouver le type réel d'un objet qui se trouve dans une collection d'objets du type de base, c'est qu'il y a - quelque part - une problème de conception...

    Ceci dit, il existe plusieurs solutions pour - éventuellement - décider le type dérivé dans lequel il faut "downcaster" le type de base, afin d'être en mesure d'accéder à une méthode particulière... Mais ce n'est généralement par recommandé
    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

  3. #3
    Membre éclairé
    Avatar de Mindiell
    Profil pro
    Inscrit en
    Juin 2006
    Messages
    735
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 735
    Par défaut
    Ah ah ! Tu m'intéresses !
    Un problème de conception ? Alors je t'explique mon idée :

    J'ai une classe principale Vehicule
    Je crée d'autres classes Voiture, Camion, etc...
    Je crée des sous-classes de Voiture (Berline, Sport, etc...) et de Camion, etc...
    etc...

    Maintenant, je veux pouvoir gérer aussi bien de simples voitures que des berlines, des camions, etc... J'ai donc créé une classe pour gérer ca et qui contient un tableau de Vehicule*
    Ca me permet donc de placer dedans aussi bien des Berline que des Camion, ok ?

    Maintenant, lors de l'utilisation de mon appli, je veux pouvoir dire que ma Berline qui s'appelle "1234 XY 98" (la plaque c'est son identifiant) je veux lui faire faire quelque chose de particulier. J'ai donc penser à une fonction GetElementByName qui me permet de retrouver cet objet précisément et donc de pouvoir l'utiliser. Un peu comme en DOM dans le web :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    if (machin)
    {
     MonManager.GetElementByName("1234 XY 98")->MethodeSpecifiqueBerline();
    }
    C'est pas possible ca ?

  4. #4
    Expert confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2003
    Messages
    3 549
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Décembre 2003
    Messages : 3 549
    Par défaut
    MonManager.GetElementByName("1234 XY 98") retournera a priori un Vehicule et non une Berline.
    En effet, il est fort possible que cette plaque soit celle d'un autre type de véhicule.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 633
    Par défaut
    Ce qui m'intéresserait de savoir c'est, dans quel cas il est important de pouvoir appeler une méthode spécifique aux berlines sur un objet spécifique d'une collection de véhicule

    N'oublie jamais que, bien souvent, si une classe a plus d'une responsabilité, c'est qu'elle en a sans doute trop

    Si, quelque part, tu a décidé de faire passer n'importe quel type de véhicule pour... un véhicule, c'est que tu estimais que tu pouvais tous les gérer comme tel (les camion, les voitures, les vélos, les patins à roulettes ...)

    Cela signifie que, si tu dois pouvoir gérer différemment les différents types de véhicule, c'est qu'il te faut une classe pour gérer chaque type distinctement, quitte à prévoir une classe qui traite les véhicules de manière plus générique (et sans doute une information permettant, sur base d'un véhicule générique, de savoir de quel type de véhicule il s'agit réellement)

    N'oublie pas non plus que, finalement, qu'il s'agisse d'une berline, d'une voiture de sport ou d'un "mono space", tu ne fais que modifier les caractéristique de la voiture, mais que ca reste... une voiture

    Je dirais presque que le fait qu'une voiture soit une berline, d'un modèle "sport", un monospace ou ... ne fait que représenter un état particulier du véhicule reconnu comme "voiture"

    Tu en viendrais presque à devoir envisager une logique du genre 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
    si (LeVehicule est un camion)
    {
        demander à GestionnaireCamion de me donner le camion (identité)
        travailler sur le camion reçu
    }
    sinon si (LeVehicule est une voiture)
    {
        demander à GestionnaireVoiture de me donner la voiture (identité)
        travailler sur la voiture reçue
    }
    sinon si (LeVehicule est un char à voile)
    {
        demander à GestionnaireCharAVoile de me donner le char à voile (identité)
        travailler sur le char à voile reçu
    }
    ...
    Un joli mélange du pattern "mediator" et du pattern "chain of responsability" serait de nature à te permettre ce genre de chose

    EDIT la précision qui peut manquer:

    Le pattern "mediator" sera de nature à permettre à tous les gestionnaires particuliers de discuter avec le gestionnaire générique (et inversément) et le pattern "chain of responsability" serait de nature à permettre au gestionnaire générique de manipuler tous les véhicules particuliers
    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

  6. #6
    Membre éclairé
    Avatar de Mindiell
    Profil pro
    Inscrit en
    Juin 2006
    Messages
    735
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 735
    Par défaut
    Bon, je comprends un peu.
    En même temps c'est pour des tests que j'ai besoin d'un tel accesseur pour le moment, donc pas hyper important.

    Je marque résolu, et je reviendrai voir ca après les fêtes voir si je comprends mieux

    Merci !

  7. #7
    Membre éclairé
    Avatar de Kalite
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2006
    Messages
    310
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Octobre 2006
    Messages : 310
    Par défaut
    Salut

    Comme la dit koala01 tu a un problème de conception certain. Après je ne connai pas les deux patern qu'il a citer. Moi je t'aurai plutot aiguillé sur un patern "visiteur".

    Tu trouvera tous ce que tu a besoin concernant les paterns sur le net.

    Salut.

  8. #8
    Membre éclairé
    Avatar de Mindiell
    Profil pro
    Inscrit en
    Juin 2006
    Messages
    735
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 735
    Par défaut
    Me revoilà, après les fêtes !
    Merci pour vos réponses, et moi je vois que mon explication est bien male faite. Dans mon cas, je fais une GUI. Dans ce cas là, tous mes objets se basent sur ma base virtuelle "conteneur".

    Lorsque, dans mon "main", je veux lancer une fonction (suite à l'appui d'une touche par exemple) d'un objet en particulier, je souhaitais pouvoir faire cela
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Root.GetElementByName("Bouton_1").SetText("Clique-zmoi");
    Je suis désolé de vous avoir déporté vers le sujet "voitures" ce qui n'était peut-être pas la meilleure idée pour me faire bien aider

    Donc, voilà. Mon objet "Root" est un objet un peu particulier qui contient un tableau de pointeurs type cBase* et je voudrais bien accéder à mes éléments une fois insérés dans le tableau

    Merci beaucoup d'avance de vos idées/opinions/critiques/etc...

  9. #9
    Membre Expert
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Par défaut
    Classiquement, dans ces cas-là, on stocke une propriété sur l'objet, qui donne le type réel de l'instance. Ensuite, on teste cette propriété et on caste en fonction. C'est ce que fait DOM, notamment.

    Sinon, tu peux aussi utiliser des interfaces (le problème sera le même, mais ça limite les cas particuliers), par exemple, dans ton cas, tu pourrais avoir une interface ControlWithText, qui définit des méthodes virtuelles pures GetText et SetText, et un moyen de tester si un objet CBase* implémente l'interafce ControlWithText (par exemple, une fonction membre). En passant par des smart pointers, tu peux en plus jouer sur l'opérateur de cast et de comparaison à NULL (méthode COM), et faire des choses du type :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    CControlWithTextPtr ctrlText = static_cast<CControlWithTextPtr>(ctrl);
    if(ctrlText != NULL)
    {
       ...
    }
    L'autre solution, la plus mauvaise à mon avis, consiste à mettre dans la classe de base l'ensemble des méthodes possibles, même si elles n'ont pas de sens sur l'objet. Cette méthode est très répandue, et c'est à mon avis la pire. À éviter.

    Contrairement à ce que d'autres ont dit, je ne pense pas que ça témoigne nécessairement d'un problème de conception. Il y a des cas (typiquement, arbre d'interprétation d'un langage, gui...) où l'on a besoin de collections d'objets faiblement typés, et l'on a besoin d'un traitement particulier suivant le type réel de l'objet. Cela dit, je regarde de plus en plus du côté de boost::any pour ce genre de cas (plus extensible, je pense).

  10. #10
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 391
    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 391
    Par défaut
    N'est-ce pas plutôt un dynamic_cast que tu voulais montrer?
    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.

  11. #11
    Membre Expert
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Par défaut
    Effectivement... J'ai un peu trop pensé à COM et un peu oublié dynamic_cast quand j'ai écrit mon message. Les smart pointers de COM simulent le comportement du dynamic_cast, mais avec des cast statiques.

    Cela dit, dynamic_cast ne permet pas à ma connaissance de faire un cast en une interface non dérivée du type de base. Donc dans le cas d'un objet implémentant plusieurs interfaces, ce n'est inutilisable (au contraire de l'opérateur as de C#, qui pour le coup est bien pratique).

  12. #12
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 391
    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 391
    Par défaut
    Il me semble que si, le dynamic_cast est supposé permettre ça, je crois que ça s'appelle le cast croisé...
    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 Expert
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Par défaut
    Il me semble que si, le dynamic_cast est supposé permettre ça, je crois que ça s'appelle le cast croisé...
    J'étais persuadé que ça ne fonctionnait pas... Et après vérification, ça fonctionne effectivement très bien.

  14. #14
    Membre éclairé
    Avatar de Mindiell
    Profil pro
    Inscrit en
    Juin 2006
    Messages
    735
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 735
    Par défaut
    Merci,

    J'avoue que là je suis un peu largué par les notions d'interface.
    J'ai essayé ceci qui fonctionne :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    //Je mets un void* pour cette méthode particulière
    void* cBase::GetElementByName (std::string name);
    // Et plus tard je cast le résultat de la méthode
    main()
    {
     ...
     ((CButton*)Root.GetElementByName("MonBouton"))->MethodeSpecifiqueBouton();
     ...
    }
    J'ai lu sur ce forum que void* était à éviter en C++ (je crois).
    J'ai aussi l'impression que mon cast devrait se faire via un static_cast ou un dynamic_cast (la différence ? faut que j'aille me renseigner sur le net moi )

    Bref, que pensez-vous de cette solution ? Mon but étant que l'utilisation dans le main soit la plus simple possible.

    Merci beaucoup !

  15. #15
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 391
    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 391
    Par défaut
    Si tu es certain que ce sera systématiquement un CButton, tu peux utiliser un static_cast<>. S'il y a une possibilité que ça ne soit pas le cas, utilise un dynamic_cast<> et vérifie que le pointeur obtenu n'est pas nul.
    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.

  16. #16
    Membre éclairé
    Avatar de Mindiell
    Profil pro
    Inscrit en
    Juin 2006
    Messages
    735
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 735
    Par défaut
    Eh bien, ce n'est pas "certain", dans le sens ou je peux toujours vouloir faire un cast d'un Textbox vers un Button. Mais dans ce cas là, c'est moi qui suis en tort, non ?
    Le null est bien evidemment à tester car si la fonction ne trouve pasl'élément elle retourne bien Null.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 633
    Par défaut
    Mais, ceci dit...

    Dans quelle mesure ne devrais tu pas jamais accéder au bouton qui se trouve dans un conteneur

    En effet, une fois le bouton créé (et les différentes propriétés qui s'y rapportent réglées), tu ne devrais plus devoir y toucher pour autre chose que lui donner/retirer le "focus"

    C'est donc au moment de la récupération de l'interface qu'il s'agit de fonctionner sur un mode 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
    17
    18
    Object* o;
    lecture type_reel
    factory->create(type_reel)
    si (type_reel == "Button")
    {
        lecture donnees
        Button* b =<static_cast Button*>(o);
        b->setText(/*...*/);
        b->setPos(x,y);
        b->setColor(R,G,B);
        /*...*/
    }
    else if(type_reel=="Label")
    {
        /*...*/
    }
    /*...*/
    Window->registerItem(o);
    Où Window est ici le conteneur concerné pour lequel registerItem ajoute un pointeur sur un objet existant dans une liste (idéalement doublement chainée et circulaire, triée par ordre de tabulation )

    Par la suite, ce sont les événements sur la (partie de la)fenêtre qui prennent le relais sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    actuel->setFocus(false); /* retrait du focus de l'élément précédemment 
                              * sélectionné */
    ++actuel; /* sélection de l'élément suivant (pourrait etre celle de l'élément
               * précédent */
    actuel->setFocus(true);
    ou actuel est un pointeur sur un objet du type de la classe de base, et ou setFocus est une méthode (pas forcément polymorphe, bien que faisant sans doute appel à une fonction redraw() qui l'est ) propre... à la classe de base

    Et, pour finir, c'est l'élément sélectionné qui envoie l'événement Clicked (ou tout autre) vers le gestionnaire d'événements approprié
    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 éclairé
    Avatar de Mindiell
    Profil pro
    Inscrit en
    Juin 2006
    Messages
    735
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 735
    Par défaut
    Et quid si le clic sur un bouton doit modifier le text d'un label ?

    Outre le fait que je n'ai pas bien compris ta première phrase (double, voire triple négation ), ma gui est très simple pour le moment. Lorsque je crée les éléments j'utilise le bon type d'objet (ce n'est pas un souci) que j'ajoute effectivement à une liste globale située, elle, dans mon élément Root. Ce derniersert uniquement à gérer la gui et n'est jamais dessiné. Il reste cependant le père des autres et dérive aussi de cBase.

    J'arrive à utiliser des évènements sans problème qui appellent des fonctions utilisateurs telles "void MaFonction (cButton* Moi, Event EvenementDeclencheur)" qui sont, bien entendu, spécifique à l'objet qui les appelle (ici un bouton donc).

    Je n'ai donc pas de gestionnaire d'evènements à proprement parler...

Discussions similaires

  1. retour tableau d'objets par service web axis jboss
    Par TrollMaster dans le forum XML/XSL et SOAP
    Réponses: 6
    Dernier message: 27/11/2005, 21h45
  2. Tableau d'objets
    Par moulefrite dans le forum MFC
    Réponses: 7
    Dernier message: 15/06/2004, 14h14
  3. Sauvegarde / Chargement d'un tableau d'objets
    Par Naruto dans le forum Langage
    Réponses: 3
    Dernier message: 18/05/2004, 14h34
  4. [VB6]Tableau d'objet withevents
    Par soazig dans le forum VB 6 et antérieur
    Réponses: 8
    Dernier message: 13/02/2004, 19h44
  5. [VB6] [Syntaxe] Fonction renvoyant un tableau d'objets
    Par Troopers dans le forum VB 6 et antérieur
    Réponses: 2
    Dernier message: 18/10/2002, 15h33

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