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 :

Question map avec pointeur sur class.


Sujet :

C++

  1. #1
    Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2012
    Messages
    118
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2012
    Messages : 118
    Points : 40
    Points
    40
    Par défaut Question map avec pointeur sur class.
    Bonjour,
    Je souhaite réaliser un jeux vidéo. Le jeux vidéo lira une mappe et fera apparaître des énemis en fonction de cette mappe. Par exemple si à la case [y][x] il y à le caractère X ça va créer un monstre X si c'est un B ça va créer un monstre B. J'ajouterait ensuite ce monstre à ma liste.
    Le problème est que si il y a plusieurs monstres, une dizaine par exemple, je n'ai pas envie de faire : if (c'est x) alors tu me créer le monstre x dix fois (un pour chaque lettre).
    Je voudrais savoir comment vous feriez.
    J'avais pensé à faire une map avec en key la lettre et en value le pointeur sur ma class monstre X.
    Voilà ma question.
    Merci pour vos réponses.
    Edit: La question la plus importantes si je fait avec map, peut-on faire par exemple si j'ai une map qui se nomme toto:
    Monstre Mx = new toto[x];

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,

    Oui, la map <clé, Monster*> est une possibilité de travailler, mais non, pas de la manière dont tu semble vouloir t'y prendre.

    D'abord, il faut savoir que l'opérateur [] a un comportement très particulier au niveau de std::map : Si la clé indiquée n'existe pas, il créera la clé en question en laissant la valeur non initialisée.

    Comme la valeur est un pointeur, le pointeur correspondant à la clé sera NULL (nullptr en C++11).

    Utiliser l'opérateur [] pour accéder aux éléments de ta map serait donc une idée particulièrement mauvaise car il est tellement facile d'oublier d'insérer une clé (ou même simplement d'écrire X au lieu de C) que cela te promettrait de longues heures de déboggage à t'arracher les cheveux.

    Le meilleur moyen est encore d'utiliser les itérateurs et la fonction find, sous une forme proche de (C++11 inside)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    auto const & it = laMap.find(value);
    if(it != laMap.end()){
        /* l'élément existe, on peut travailler avec ici */
    }/* else{
        l'élément n'existe pas, il y a peut etre quelque chose à faire ici
    } */
    Ensuite, il faut savoir que new s'attend à être suivi d'un identifiant de type.

    Or laMap['c'] renverra... un pointeur. Et qui plus est, un pointeur de type Monster (le type de base).

    Dans le meilleur des cas, new laMap['c'] te permettra donc de créer un nouvel objet de type Monster et non un objet de type MonsterC.

    Pour résoudre ce problème, il faut recourir au concept d'élément clonable.

    L'idée est d'exposer un comportement polymorphe (typiquement une fonction clone() ) qui renvoie un pointeur du type de base (ici Monster) pointant vers un objet du type réellement attendu.

    En gros, cela prendrait 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
    class Monster : private NonCopyable /* (*) */{
        public:
            virtual ~Monster(){}
            /* aucune implémentation possible pour le monstre de base */
            virtual Monster * clone() const = 0;
    };
    class MonsterA : public Monster{
        public:
            Monster * clone() const{return new MonsterA ;}
    };
    class MonsterB : public Monster{
        public:
            Monster * clone() const{return new MonsterB ;}
    };
    class MonsterC : public Monster{
        public:
            Monster * clone() const{return new MonsterC ;}
    };
    class MonsterD : public Monster{
        public:
            Monster * clone() const{return new MonsterD ;}
    };
    (*) Typiquement, les classes qui interviennent dans une hiérarchie de classe ont sémantique d'entité.

    Ce sont des classes qui ne sont pas sensées être copiable (ni affectable) afin d'assurer "l'intégrité référentielle".

    Si tu crées une classe proche de (c++11 inside)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    NonCopyable{
        public:
            NonCopyable(NonCopyable const &) = delete;
            NonCopyable & operator=(NonCopyable const &) = delete;
        protected:
            NonCopyable(){}
            ~NonCopyable(){}
    };
    tu peux utiliser l'héritage privé pour chaque classe non copiable et non affectable que tu envisageras

    Au final, la logique que tu devrais suivre serait donc proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    auto const & it = laMap.find(value);
    if(it != laMap.end()){
        Monster * temp = it.second->clone();
        /* tu enregistre temp où c'est nécessaire -) */
    }/* else{
        l'élément n'existe pas, il y a peut etre quelque chose à faire ici
    } */
    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 du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2012
    Messages
    118
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2012
    Messages : 118
    Points : 40
    Points
    40
    Par défaut
    Merci pour la réponse détaillée. J'ai des questions suite à votre message.
    Vous avez dit:
    Or laMap['c'] renverra... un pointeur. Et qui plus est, un pointeur de type Monster (le type de base).
    Ce que je voulais faire c'était par exemple faire:
    toto['X'] = MonsterA;
    toto['Y'] = MonsterB;
    .... .
    Donc en fesant Monster *lol = new toto['X'];
    Je fait un new sur MonsterA;
    Donc du coup j'aurais du récupérer un MonsterA ou MonsterB et non pas un Monster tout court, non ?
    Et aussi:
    if(it != laMap.end())
    M'évitera cette erreur:
    D'abord, il faut savoir que l'opérateur [] a un comportement très particulier au niveau de std::map : Si la clé indiquée n'existe pas, il créera la clé en question en laissant la valeur non initialisée.

  4. #4
    Membre confirmé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2008
    Messages
    308
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Finance

    Informations forums :
    Inscription : Février 2008
    Messages : 308
    Points : 622
    Points
    622
    Par défaut
    Pourquoi ne pas faire une fonction

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    Monstre* GenererMonstre(std::string type){
    	if(type == "A") return new MonstreA();
    	else if(type == "B") return new MonstreB();
    	else if(type == "C") return new MonstreC();
    	else if(type == "D") return new MonstreD();
    	else return NULL;
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    marvar = GenererMonstre("A");
    de toutes façon avec ta technique tu dois forcement écrire une ligne pour chaque type de monstre
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    monstres["A"]= MonstreA;
    monstres["B"]= MonstreB;
    monstres["C"]= MonstreC;
    monstres["D"]= MonstreD;
    @koala01 : du coup il faut déjà avoir au moins une instance de chaque type avec ta technique, c'est pas l’idéal d’après moi

  5. #5
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Salut

    Citation Envoyé par koala01 Voir le message
    D'abord, il faut savoir que l'opérateur [] a un comportement très particulier au niveau de std::map : Si la clé indiquée n'existe pas, il créera la clé en question en laissant la valeur non initialisée.

    Comme la valeur est un pointeur, le pointeur correspondant à la clé sera NULL (nullptr en C++11).
    Juste un petit détail, j'ergote : ce n'est pas exact de dire que la valeur n'est pas initialisée. Si elle ne l'était pas, un pointeur dans une paire <clé, valeur> qui vient d'être créée par [] ne serait pas nul. Il est bien initialisé à nullptr. De même, les nombres sont initialisés à 0.

    Bien sûr, cela ne change rien au reste de l'explication !
    Find me on github

  6. #6
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut
    Citation Envoyé par shirohige Voir le message
    Ce que je voulais faire c'était par exemple faire:
    toto['X'] = MonsterA;
    toto['Y'] = MonsterB;
    .... .
    Donc en fesant Monster *lol = new toto['X'];
    Je fait un new sur MonsterA;
    Donc du coup j'aurais du récupérer un MonsterA ou MonsterB et non pas un Monster tout court, non ?
    Tu ne peux pas faire une map de types.
    Koala01 te propose de stocker des monstres différents (donc des instances de classes, des objets) dans la map, puis de les copier quand tu crée un monstres.

    Tu récupère un Monstre * en faisant un find. clone est une fonction virtuelle, c'est l'implémentation de la classe fille correspondante qui sera appelée.

    A chaque coup qu'un sujet comme ça ressort, j'ai la même réaction : l'héritage au niveau de Monstre est inutile.
    Mais que tu partes sur de l'héritage ou pas, la proposition de koala01 est bonne et marchera.

  7. #7
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par bebaijhi Voir le message
    @koala01 : du coup il faut déjà avoir au moins une instance de chaque type avec ta technique, c'est pas l’idéal d’après moi
    Et pourtant :
    • Elle répond strictement à la question posée par shirohige
    • Elle correspond "trait pour trait" en interne à une implémentation "classique" de la fabrique.

    L'idée est de s'assurer d'avoir un "modèle" de chaque monstre dans la map, et de rechercher le monstre à construire afin de le cloner à la demande

    Nous devrons, effectivement, faire en sorte d'avoir un élément de chaque type dérivé, mais, si l'on applique le patron factory jusqu'au bout, cela devient quelque chose comme
    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
    class Factory{
        public:
            /*static */ void register(KEY key, Monster const& monster){
                if(laMap.find(key)!=laMap.end()){
                    laMap.insert(std::make_pair(key, monster.clone());
                }
            }
           Monster * create(KEY key){
                auto const & it =laMap.find(key);
                if(it!=laMap.end()){
                   return it.second->clone()
                }
              return nullptr;
            }
        private:
             static std::map<KEY, Monster *> laMap;
    };
    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

  8. #8
    Membre confirmé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2008
    Messages
    308
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Finance

    Informations forums :
    Inscription : Février 2008
    Messages : 308
    Points : 622
    Points
    622
    Par défaut
    Je ne connaissait pas cette implémentation de la factory avec clone
    J'utilise la même technique que celle proposée par Flob90 dans ce post http://www.developpez.net/forums/d11...ttern-factory/

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

Discussions similaires

  1. Réponses: 9
    Dernier message: 18/01/2009, 18h16
  2. [POO] Classe avec pointeurs de classes
    Par dridri dans le forum Langage
    Réponses: 3
    Dernier message: 15/02/2008, 17h53
  3. Find() avec predicate sur classe personnalisée
    Par Ivynox dans le forum Windows Forms
    Réponses: 1
    Dernier message: 06/02/2008, 11h21
  4. Réponses: 4
    Dernier message: 15/10/2006, 18h05
  5. Problème de pointeurs sur classe
    Par fabiin dans le forum Delphi
    Réponses: 1
    Dernier message: 05/08/2006, 18h13

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