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 :

Agir sur des instances de manière générale


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre régulier
    Inscrit en
    Novembre 2007
    Messages
    9
    Détails du profil
    Informations forums :
    Inscription : Novembre 2007
    Messages : 9
    Par défaut Agir sur des instances de manière générale
    Bonjour à tous,

    Ma question va peut être paraitre stupide, mais j'ai du mal à trouver après moult recherches comment résoudre ce simple problème :

    J'ai une classe qui contient un objet et une map :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    class Salle {
    private:
      Personne *personne;
      map<char*,Objet*> *listeObjets;
    public:
      bool regarder(Personne*,Objet*);
      void boucle();
    };
    Le soucis c'est la fonction regarder(), comment je peux faire en sorte d'utiliser cette fonction pour qu'elle agisse sur n'importe qu'elle instance de la classe Objet ?

    En gros si je crée plusieurs objets (en alimentant ma map) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    Salle::Salle() {
      listeObjets["objet1"] = new Objet(1,"Chaise");
      listeObjets["objet2"] = new Objet(2,"Table");
      listeObjets["objet3"] = new Objet(3,"Armoire");
    }
    comment je peux faire que je puisse utiliser regarder() avec comme deuxième paramètre une sorte de référence à la classe, un joker, donc que je ne sois pas obligé d'appeler cette fonction pour chaque instance qui se trouvent dans la map ?

    Faire quelque chose comme ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    void Salle::boucle() {
    while(true) {
      // Ici l'action se déclenche quand la personne regarde un Objet sans savoir duquel il s'agit
      if(regarder(personne,**Objet**)) {
        cout << "La personne regarde un objet" << endl;
      }
    }
    Au lieu de ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    void Salle::boucle() {
    while(true) {
      // Ici l'action se déclenche quand la personne regarde un Objet sans savoir duquel il s'agit
      if(regarder(personne,listeObjets["objet1"])) {
        cout << "La personne regarde un objet" << endl;
      }
      if(regarder(personne,listeObjets["objet2"])) {
        cout << "La personne regarde un objet" << endl;
      }
      if(regarder(personne,listeObjets["objet3"])) {
        cout << "La personne regarde un objet" << endl;
      }
    }
    Surtout que le nombre d'objet peut varier...

    J'espère que j'ai été clair dans l'explication du problème, merci d'avance pour votre aide.

  2. #2
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 395
    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 395
    Par défaut
    Je ne comprends pas le problème.

    Cherches-tu à tester en boucle pour chaque objet de la map?
    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 éclairé
    Profil pro
    Inscrit en
    Février 2009
    Messages
    68
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 68
    Par défaut
    Voici une solution à base d'itérateurs sur la map:

    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
     
    void Salle::boucle() {
      while(true) {
     
        // Parcourt la map pour chercher les objets que la personne regarde
        map<char*,Objet*>::iterator ite = listeObjets.begin();   // <= iterateur de debut
        map<char*,Objet*>::iterator iteEnd = listeObjets.end();  // <= iterateur de fin
        while(ite != iteEnd) {
          if(regarder(personne,ite->second)) {   // ite->second retourne Objet* 
            cout << "La personne regarde un objet" << endl;
          }
          ++ite;     // <= itere sur la prochaine paire (char*,Objet*)
        }
      }
    }
    Note: il y a un petit problème dans ton code, tu déclares la map listeObjets comme pointeur dans la classe Salle mais tu l'utilises comme si c'était une instance ordinaire !!

    Note2: si tu ne modifies pas les éléments de la map lors de l'itération, il est préférable d'utiliser const_iterator au lieu de iterator, c'est plus optimal.

  4. #4
    Membre régulier
    Inscrit en
    Novembre 2007
    Messages
    9
    Détails du profil
    Informations forums :
    Inscription : Novembre 2007
    Messages : 9
    Par défaut
    Citation Envoyé par KorWipe Voir le message
    Voici une solution à base d'itérateurs sur la map:
    Note: il y a un petit problème dans ton code, tu déclares la map listeObjets comme pointeur dans la classe Salle mais tu l'utilises comme si c'était une instance ordinaire !!

    Note2: si tu ne modifies pas les éléments de la map lors de l'itération, il est préférable d'utiliser const_iterator au lieu de iterator, c'est plus optimal.
    Oui donc en gros je suis obligé de tester pour chaque instance, je commençais à me dire que c'était la seule possibilité. Merci aussi pour les remarques sur les pointeurs et l'itérateur, j'en prends bonne note ^^ Par contre les instances ne sont pas modifié pendant l'itération mais peuvent l'être dans le while de la méthode Salle:boucle(), ça change quelque chose à l'utilisation du const_iterator ?

  5. #5
    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
    Citation Envoyé par Metallizer Voir le message
    Oui donc en gros je suis obligé de tester pour chaque instance, je commençais à me dire que c'était la seule possibilité.
    Tu peux aussi jouer avec les algos de la STL (for_each) et/ou avec les iterateurs de boost(filter_iterator)

    Citation Envoyé par Metallizer Voir le message
    Merci aussi pour les remarques sur les pointeurs et l'itérateur, j'en prends bonne note ^^
    Pourquoi utiliser des pointeurs et par directement des objets ?
    Pourquoi utiliser des char * et pas des std::string ?
    Si tu veux malgré tout en rester au char*, ne serait-il pas plus pertinent d'utiliser des const char * ?

    Citation Envoyé par Metallizer Voir le message
    Par contre les instances ne sont pas modifié pendant l'itération mais peuvent l'être dans le while de la méthode Salle:boucle(), ça change quelque chose à l'utilisation du const_iterator ?
    Pour résumer :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    map<Type1,Type2>::iterator it = mon_map.begin();
    (*it).second = Type2(); // OK
      // ou toute autre méthode non const sur de Type2
     
    map<Type1,Type2>::const_iterator it = mon_map.begin();
    (*it).second = Type2(); // interdit
      // ou toute autre méthode non const sur de Type2
    Mais comme tu utilises des pointeurs, cela devient plus problématique :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
       std::map<Type1, Type2*>::const_iterator it1 = mon_map.begin();
       (*it1).second = new B(); // interdit
     // En revanche :
       *((*it1).second) = B(); // OK
         // ou toute autre méthode non const sur de Type2
    Conclusion : as-tu vraiment besoin d'avoir des pointeurs sur Objet ?

  6. #6
    Membre éclairé Avatar de Trunks
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2004
    Messages
    534
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mai 2004
    Messages : 534
    Par défaut
    Juste une remarque hors sujet si je peux me permettre.

    Je vois souvent (*it).methode() alors qu'on peut utiliser it->methode() qui selon moi est plus claire et simple. Y a-t-il une raison particulière?

  7. #7
    Membre régulier
    Inscrit en
    Novembre 2007
    Messages
    9
    Détails du profil
    Informations forums :
    Inscription : Novembre 2007
    Messages : 9
    Par défaut
    Merci à tous pour vos réponses, je n'ai pas poussé si loin le concept comme le fait koala.
    Pour les strings, peut être à tort, je me suis dit qu'un char* prenait moins de place en mémoire qu'un std::string.
    Sinon pour les pointeurs, je pense que c'est une question d'habitude pour moi, si je me réfère à UML, j'utilise un pointeur quand je fais une simple association et un objet directement instancié sans pointeur quand c'est un lien d'agrégation voire de composition (destruction du conteneur entraine destruction du contenu). Dans le cas de ma salle, ça se justifierait complètement de ne pas utiliser de pointeur en effet...

    En tout cas merci pour vos explications et vos exemples de code, je vais approfondir tout ça histoire d'avoir le plus de souplesse possible.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut, et bienvenue sur le forum

    Je ne peux m'empêcher une paire de remarques...

    Primo (c'est fout ce que j'ai l'impression de me répéter aujourd'hui ), tu devrais préférer l'utilisation de la classe string, fournie par le standard dans l'espace de nom std et disponible par simple inclusion du fichier d'en-tête <string> pour la gestion de chaines de caractères...

    Elle apporte énormément d'avantages par rapport à la gestion de chaines de caractères "C style" du fait que tu n'a, entre autre, plus besoin de gérer par toi-même et de manière dynamique la mémoire nécessaire au maintient de son contenu, mais aussi parce qu'elle défini les opérateur <, >, == qui sont particulièrement intéressant

    Ensuite, comme l'un des objectifs de ta map est de servir sous la forme d'une "factory" (un "desing pattern" régulièrement rencontré), il serait peut être intéressant de rendre tes objets dérivés clonables, sous une forme 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
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    class Objet
    {
        public:
            Objet(std::string const& t /*,... */): mtype(t) /* ,... */
            {
            }
             Objet(Objet const & rhs) :mtype(rhs.t)/* , ... */ 
             virtual ~Objet(){}
            std::string const & objectType() const{return mtype;}
            virtual Objet* clone() = 0; // #1
        private:
            std::string mtype;
    };
    class Table : public Objet
    {
        public:
            Table(/*.....*/ ) : Objet("table" /*, ...*/)/*, autres membres */
            {
            }
            Table(Table const & t):Objet(t)/*, autres membres */
            {
            }
            virtual ~Table(){}
            virtual Table* clone() const {return new Table(*this);} // # 2, #3
    };
    class Chaise : public Objet
    {
            Chaise (/*.....*/ ) : Objet("chaise" /*, ...*/)/*, autres membres */
            {
            }
            Chaise (Table const & t):Objet(t)/*, autres membres */
            {
            }
            virtual ~Chaise(){}
            virtual Chaise * clone() const {return new Chaise (*this);} // #2, # 3
    };
    /* et ainsi de suite */
    Avec les remarques "intéressantes"

    #1 : ce genre de fonction s'appelle une fonction virtuelle pure... Comme le mot clé virtual l'indique, il s'agit d'une fonction qui a vocation à être polymorphe (à s'adapter au type d'objet réel), mais pour laquelle nous signalons au compilateur que nous ne disposons pas de suffisemment d'information - si on ne regarde que la classe mère, pour implémenter correctement le comportement.

    L'éditeur de lien ne cherchera donc pas après l'implémentation de la méthode, mais, comme le compilateur a horreur du vide (et que cette méthode non défini laisse un vide dans la représentation de Objet), il refusera toute tentative de créer une instance de la classe.

    #2 Pour permettre de créer une instance (de chaise ou de table, qui sont deux... objets ), il faut donc impérativement définir la méthodes en veillant à ce que le comportement soit adapté au type réel (à une table si on a une table devant les yeux, à une chaise si c'est... une chaise )

    #3 Cela ne t'aura pas échappé, le type de retour de la méthode clone() est cohérent par rapport au type réel: c'est un objet lorsque nous définissons la classe objet, une Table lorsque nous définissons Table et une chaise lorsque nous définissons Chaise...

    Cela s'appelle le retour co-variant.

    C'est autorisé parce que table et chaise héritent de objet et parce que la valeur de retour se fait sous la forme d'une référence ou d'un pointeur...

    Il est en effet logique que si nous demandons à une table de se cloner (de créer une copie d'elle-même), elle renvoie un pointeur sur un objet... table (même si ce pointeur peut décider de se faire passer pour un objet )

    Cela te permettra d'envisager un code 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
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
     
    class Salle
    {
        Personne mper;
        std::map<std::string, Objet*> mobj;
        public:
            Salle(Personne const &p):mper(p)
            {
                 /* personnellement, je préfère le recours à insert, qui permet
                  * au moins de se rendre compte qu'il s'agit bel et bien de l'insertion
                  * d'un nouvel élément 
                  */
                mobj.insert(std::make_pair("table",new Table()));
                mobj.insert(std::make_pair("chaise", new chaise()));
                /* les autres objets */
            }
            Table* giveMeATable() const
            {
                std::map<std::string,Objet*>::const_iterator it = mobj.find("table");
                return (*it).second->clone();
            }
            Chaise* giveMeASeat() const
            {
                std::map<std::string,Objet*>::const_iterator it = mobj.find("chaise");
                return (*it).second->clone();
            }
            /* voir, pourquoi pas, même si ce n'est pas conseillé ;) */
            std::vector<Objet*> oneFromAll() const
            {
                 std::vector<Objet*> tab;
                 for(std::map<std::string, Objet*>::const_iterator it =mobj.begin();
                     it!=mobj.end();++it)
                     tab.push_back((*it).second->clone());
            }
            ~Salle()
            {
                 for(std::map<std::string, Objet*>::const_iterator it =mobj.begin();
                     it!=mobj.end();++it)
                     delete (*it).second;
            }
    };
    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

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

Discussions similaires

  1. Réponses: 2
    Dernier message: 17/03/2011, 14h13
  2. Information sur L'ETL de manière générale
    Par funckfot dans le forum Approche théorique du décisionnel
    Réponses: 1
    Dernier message: 22/04/2010, 15h10
  3. [cURL] agir sur des sites en Java ou ASP
    Par nightcyborg dans le forum Bibliothèques et frameworks
    Réponses: 1
    Dernier message: 25/04/2008, 08h43
  4. agir sur des string
    Par 20100. dans le forum Macros et VBA Excel
    Réponses: 2
    Dernier message: 14/02/2008, 14h49
  5. [Joomla!] critères sur des articles à la manière du ranking
    Par Nillak dans le forum EDI, CMS, Outils, Scripts et API
    Réponses: 2
    Dernier message: 19/03/2007, 10h33

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