Partage
  • Partager sur Facebook
  • Partager sur Twitter

Les vectors, fonctionnement ?

SDL et vectors

Sujet résolu
    13 juin 2007 à 2:11:15

    Bonjour à tous !
    J'essaye de gérer les vectors pour rendre le plus propre possible mon programme.

    Tout d'abord je me pose quelques questions :
    J'ai une classe de Base Personnage, puis des classes dérivées : PJ et PNJ. J'ai des objets de ces deux types, ai-je le droit de créer un vector de type Personnage qui contiendra soit des PJ, soit des PNJ et ensuite d'utiliser les fonctions propres à la classe de l'objet ? ou alors il me faut un vector par classe dérivée ?

    J'ai fait quelques tests et apparement, il est impossible de faire cela (peut-être faut-il faire un transtypage d'objets?!) donc j'ai opté pour la solution suivante : je créé un vector de chaque classe dérivée.

    (Pour l'instant, je ne m'occupe que des PNJ)
    Je déclare mon vector :
    vector<PNJ> vPnj;


    Je l'instancie :
    vPnj.push_back(PNJ("Ressources/Images/PNJ/pnj1.png", couleurNoire, 550, 300, 'h', 3, 4));
        vPnj.push_back(PNJ("Ressources/Images/PNJ/pnj2.png", couleurNoire, 300, 200, 'g', 3, 4));


    et dans mon main je veux faire :
    vPnj[1].DeplacementPNJ(2000,5);
            vPnj[1].Collage(ecran);


    Seulement, le programme compile mais me dégage immédiatement, pourquoi ?

    Merci d'avance !
    • Partager sur Facebook
    • Partager sur Twitter
      13 juin 2007 à 2:38:05

      Pour un vecteur qui contienne les deux, il te faudra un vecteur de pointeurs sur des personnages.

      Quant au downcasting, si tu crois en as besoin, c'est à 90% de chances une erreur de conception. Là, pas trop le temps de t'expliquer l'héritage public et le polymorphisme d'inclusion. Fais des recherches sur d'autres forums, c'est un sujet qui revient régulièrement.
      • Partager sur Facebook
      • Partager sur Twitter
      C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
        13 juin 2007 à 12:32:33

        En bref :
        Tu crée une classe Joueur (Tes deux classes sont toute deux des joueurs, humain ou non) et tu fais dériver tes deux classe de Joueur.
        Ensuite, tu crée un vector<Joueur*>, et tu fait un dynamic_cast<PNJ*>(TonInstance) quand tu en a besoin.

        Je ne sais pas si j' ai été trés clair.
        • Partager sur Facebook
        • Partager sur Twitter
          13 juin 2007 à 14:49:52

          Non, justement je ne comprends pas trop ! J'ai trouvé quelques docs sur internet qui semble être pas mal ici mais je n'arrive pas à le mettre en pratique et d'après le premier poste que j'ai eu, je ne sais pas vers quelle solution me diriger...

          Je vais expliquer vous pourrez surement m'indiquer :
          Le fait de déclarer un vector de type Personnage (classe de base je le rappel) allègerai la tâche (entre autre pour la gestion des collisions, car a la place de passer en paramètre un vector de chaque type, je n'en passerai qu'un seul.)

          Mais dans un premier temps j'aimerai faire deux choses :
          for (int i = 1; i < vPers.size(); i++)
                      vPers[i].Collage(ecran);

          Pour afficher tous les objets à l'écran sans faire une ligne par objet (la fonction Collage() est disponible dans la classe de Base uniquement, donc Personnage)

          et en fonction du type de l'objet appeler la fonction déplacement qui correspond (par exemple, un objet de type PJ aura une fonction deplacementPJ(), un de type PNJ aura un deplacementPNJ(), dispo uniquement dans la classe qui correspond)
          A l'époque on m'avait dit que tester le type d'un objet n'était pas bien, il vallait mieux mettre un identificateur de type dans la classe.

          Donc un pointeur sur vector de type Personnage convient-il quand même dans ce cas, et quelle syntaxe faut-il que j'utilise ?
          Merci !
          • Partager sur Facebook
          • Partager sur Twitter
            13 juin 2007 à 19:49:00

            Tu peux facilement conserver dans Personnage une variable qui te diras de quel type dérivé il est :

            const int TYPE_PNJ = 1;

            class Personnage
            {
            protected:
               int m_type;
            public:
               Personnage() : m_type( 0 ) {}
               int getType() const { return m_type; }
            };

            class PNJ : public Personnage
            {
            public:
               PNJ() : m_type( TYPE_PNJ ) {}

               void deplacementPNJ();
               //...
            }

            // ... plus loin dans une fonction
            PNJ * p = 0;
            if( vPers[ x ]->getType() == TYPE_PNJ )
            {
                p = dynamic_cast<PNJ*>( vPers[ x ] );

                p->deplacementPNJ(); // N'importequoi
            }


            Ne fait pas attention à ma syntaxe, c'est plus une idée générale. Il se peut qu'une erreur flagrante s'y soit glissée je l'ai écrit directement (sans tester).
            • Partager sur Facebook
            • Partager sur Twitter
              14 juin 2007 à 1:34:56

              Citation : Bartours

              I- et en fonction du type de l'objet appeler la fonction déplacement qui correspond (par exemple, un objet de type PJ aura une fonction deplacementPJ(), un de type PNJ aura un deplacementPNJ(), dispo uniquement dans la classe qui correspond)

              II- A l'époque on m'avait dit que tester le type d'un objet n'était pas bien, il vallait mieux mettre un identificateur de type dans la classe.


              II- Non. C'est pire. Dans les deux cas, cela revient à utiliser le RTTI supporté nativement, ou de le réinventer en moins bien.
              Cas 1-, c'est utiliser dynamic_cast<> pour vérifier si tu obtiens un pointeur nul ou pas après down-casting
              Cas 2-, c'est le code de MatteX

              Relativement à tes déplacements, utiliser le RTTI, c'est l'exemple typique de mauvaise conception. J'y reviens en I- (oui, oui, je réponds dans le désordre)

              Relativement à des problèmes de collisions, le RTTI est une solution type qui a parfaitement sa place. Surtout si tu veux que la collision d'un ballon avec un personnage, n'ait pas le même effet que celle d'un ballon avec un mur, ni que celle d'un personnage avec un mur.
              Ce problème rentre dans la catégorie des problèmes de multi-dispatch, et plus précisément ici de double-dispatch. Ce sujet est revenu quelques fois ces derniers mois sur le forum C++ de développez. Régulièrement la solution du design pattern visiteur a été évoquée. Perso, je la trouve complexe et assez inadaptée s'il doit y avoir beaucoup de familles de classes différentes pouvant s'entre-choquer. Idéalement, lire un des chapitres de Modern C++ Design (d'Andrei Alexandrescu, chez Addisson-Wesley) serait une bonne chose à faire. Par contre, côté niveau, ce n'est pas pour un débutant.
              De façon similaire tout simplement, prévois une fonction qui fait tous les cast vers tous les types possibles, puis testes les deux à deux histoire de déclencher la fonction spécialisée qui va bien. Cela sera lourd à mettre en place, et pénible à maintenir, mais c'est suffisament simple pour quiconque qui sait déclarer des variables et appeler if().

              Fin de la parenthèse.

              I- C'est parti pour le grand classique. C'est tellement récurrent que je vais abréger (fais une recherche sur RTTI et virtual sur le forum C++ de developpez pour en savoir plus)

              Tout l'intérêt de l'héritage public, c'est de pouvoir utiliser de façon transparente des objets de classes filles là où l'on attend un objet d'une classe mère. (Non, ce n'est pas de réutiliser du code, mais d'être utilisé en place de).
              Pour cela, au milieu des points communs que partagent tous les types d'une hiérarchie, on va définir des points de variabilité qui vont centraliser les comportements spécialisés.

              P.ex., toutes tes bestioles peuvent aller chercher de la nourriture. Pour cela, elles devront d'abord déterminer un lieu où en trouver, ensuite s'y rendre, et enfin l'obtenir. Un chien saura qu'il trouvera de la nourriture dans sa gamelle, ou qu'il devra trouver un "maitre" faible qui ratera son JP (jet de protection) contre le quemandage. Il se déplace à 4 pattes. Un pigeon sait qu'il trouvera pas loin de lui (c'est pas des flêches les pigeons), ou éventuellement au parc. Ca marche, sautille, et vole. L'humain sait qu'il aura ce qu'il faut chez le marchand. Il marche à 2 pattes.

              Tu vois ici une fonction commune à tous : essayerDeManger() qui appelera deciderDunEndroitPourNourriture(), allerA(), manger(). Chacune de ces trois sous-fonctions sera spécialisée pour chaque type de créature.

              En C++ on fera ça en déclarant la fonction virtuelle (pure) dans la classe mère, et en la supplantant pour redéfinir son comportement afin qu'il soit vraiment spécialisé pour la classe fille.
              class Bestiole {
              public:
                  virtual ~Bestiole(); // destructeur virtuel nécessaire ici
                  bool essayerDeManger() {
                      const Endroit ou = deciderDunEndroitPourNourriture() ;
                      if (allerA(ou) && seProcurerNourriture()) {
                          manger();
                          return true;
                      } else {
                          return false;
                      }
                  }

                  virtual Endroit deciderDunEndroitPourNourriture() const = 0;
                  virtual bool allerA(Endroit const&) = 0;
                  virtual void manger() = 0;
                  virtual bool seProcurerNourriture() = 0;
              };

              class Pigeon : public Bestiole
              {
              public:
                  virtual Endroit deciderDunEndroitPourNourriture() const {
                      const Endroit parc = monParcPrefere();
                      if (estTropLoin(parc, ici()) return ici();
                      else return parc;
                  }

                  virtual bool allerA(Endroit const& ou) {
                      if (estLoin(ou, ici())
                          return volerJusqua(ou);
                      else 
                          return marcherJusqua(ou);
                  }

                  virtual void manger() { .... }
                  virtual bool seProcurerNourriture() { .... }
              private:
                  bool volerJusqua(Endroit const& ou) { .... }
                  bool marcherJusqua(Endroit const& ou) { .... }
              };


              Maintenant imagine la même chose avec des tests à la RTTI, et une classe de plus, deux classes, dix classes, ... À votre avis quelle est la solution la plus simple à faire évoluer ?

              Mots clés:
              - polymorphisme d'inclusion
              - liaison tardive/dynamique
              - virtual
              - redéfinir/supplanter (override, en anglais)
              - est-un


              EDIT: message parti trop tôt
              • Partager sur Facebook
              • Partager sur Twitter
              C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.

              Les vectors, fonctionnement ?

              × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
              × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
              • Editeur
              • Markdown