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++

  1. #1
    Futur Membre du Club
    Inscrit en
    Novembre 2007
    Messages
    9
    Détails du profil
    Informations forums :
    Inscription : Novembre 2007
    Messages : 9
    Points : 9
    Points
    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 sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 381
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 381
    Points : 41 582
    Points
    41 582
    Par défaut
    Je ne comprends pas le problème.

    Cherches-tu à tester en boucle pour chaque objet de la map?

  3. #3
    Membre régulier
    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
    Points : 75
    Points
    75
    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
    Futur Membre du Club
    Inscrit en
    Novembre 2007
    Messages
    9
    Détails du profil
    Informations forums :
    Inscription : Novembre 2007
    Messages : 9
    Points : 9
    Points
    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
    Points : 13 017
    Points
    13 017
    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
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 629
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    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;
            }
    };

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

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mai 2004
    Messages : 534
    Points : 412
    Points
    412
    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?

  8. #8
    Futur Membre du Club
    Inscrit en
    Novembre 2007
    Messages
    9
    Détails du profil
    Informations forums :
    Inscription : Novembre 2007
    Messages : 9
    Points : 9
    Points
    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.

  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
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par Metallizer Voir le message
    Pour les strings, peut être à tort, je me suis dit qu'un char* prenait moins de place en mémoire qu'un std::string.
    A tort. Surtout que tu te crées des problèmes à gérer toi même les chaînes.
    Citation Envoyé par Metallizer Voir le message
    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...
    Ca n'est pas systématique, mais souvent le conteneur a directement l'objet.
    Citation Envoyé par Metallizer Voir le message
    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.
    Bon courage

+ 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