Partage
  • Partager sur Facebook
  • Partager sur Twitter

nombre d'attributs variables dans une classe

Sujet résolu
    10 août 2021 à 1:58:23

    Bonjour, 

    J'aimerais savoir comment je pourrais déclarer un nombre d'attributs variables (genre pas des variables juste dont la quantité varie) dans mon fichier .h de ma classe. Pour que vous y voyez + clair, dans mon jeu qui se joue à plusieurs équipes j'ai une classe Game dans laquelle je voudrais avoir en attribut les objet de la classe Team dont je ne connais pas le nombre à l'avance. J'avais pensé à garder les objets Team dans un tableau qui serait alors un attribut de ma classe Game, je sais pas si c'est une bonne idée... Si vous avez des idées je suis preneur! Merci d'avance

    • Partager sur Facebook
    • Partager sur Twitter
      10 août 2021 à 3:22:52

      Salut,

      En gros, ce que  tu veux pouvoir placer dans ta classe Game, c'est une collection de Team ...

      Et, pour cela, tu n'a que l'embarras du choix, entre std::vector (tableau de taille dynamique), std::list (liste d'éléments) std::queue (file, système FIFO), std::stack (pile, système LIFO), std::set / std::map (arbres binaires de recherches), j'en passe et de meilleures, en fonction de la manière dont tu veux manipuler tes équipes et de la manière dont tu prévois essentiellement de les utiliser ;)

      -
      Edité par koala01 10 août 2021 à 3:24:06

      • Partager sur Facebook
      • Partager sur Twitter
      Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
        10 août 2021 à 9:14:48

        Bonjour,

        J'ajouterai pour ma part que std::vector est 'cache friendly alors que std::list est susceptible d' éparpiller les différents éléments dans la mémoire du processus en cours. :)

        • Partager sur Facebook
        • Partager sur Twitter

        Mon site web de jeux SDL2 entre autres : https://www.ant01.fr

          10 août 2021 à 10:32:53

          OK super! Est-ce que vous sauriez le code que je peux mettre dans le header de ma classe? Est-ce que je peux déclarer mon attribut ainsi et le "remplir" après en créant mes objets Team? 
          #ifndef DEF_GAME
          #define DEF_GAME
          
          #include "Team.h"
          #include <iostream>
          #include <string>
          
          class Game
          {
          public:
          	// methods
          	Game(); // constructeur par défaut
          	~Game(); // destructeur
          
          	int askTeamNbr();
          	void teamCreator(int teamsNbr);
          private:
          	// attributes 
          	int m_teamsNbr; 
          	vector<Team> m_teams;
          };
          
          #endif
          Dans ce cas je vois pas trop comment je peux initialiser m_teams dans mon constructeur...
          • Partager sur Facebook
          • Partager sur Twitter
            10 août 2021 à 11:44:08

            Avant toute chose, il faudra ajouter l'en-tête <vector> (#include <vector>) pour pouvoir utiliser std::vector, à moins qu'il n'ait déjà été inclus dans Team.h.

            Ensuite, je te déconseille très fortement d'utiliser la directive using namespace std; pour des raisons diverses et variées qui ont déjà été expliquées bien souvent sur le forum, mais que tu pourras aussi retrouver ==> ICI <==.

            Enfin, tu n'auras même plus besoin d'utiliser la variable m_teamsMbr car la classe std::vector (ainsi que toutes les collection de la bibliothèque standard) disposent d'une fonction membre nommée size() qui permet, à n'importe quel moment, de connaitre le nombre d'éléments dont elle dispose.

            La valeur renvoyée par cette fonction sera automatiquement mise à jour à chaque fois que tu ajouteras ou que tu retireras un élément dans la collection, si bien qu'il suffira vraiment de faire appel à cette fonction à  chaque fois que tu voudras ... connaitre le nombre d'équipes qui sont en jeu ;)

            Tout ceci étant dit, le fait est que m_teams sera automatiquement initialisé (avec 0 éléments, si tu ne donne pas d'autres indication) lorsque l'instance de ta classe Game sera créée. C'est tout à fait normal, et c'est ce à quoi on peut s'attendre.

            Les équipes devraient, si je comprends bien ta logique, être créées lors de l'appel à la fonction teamCreator, et, pour y arriver, il "suffira" d'ajouter dans m_teams autant d'équipes que demandées grâce à l'argument teamsNbr.

            Cet ajout ce fera sans doute à l'aide d'une boucle et de la fonction membre de std::vector push_back ou de la fonction  (membre de std::vecctor) emplace_back, sous une forme qui serait sans doute proche de

            void Game::teamCreator(int teamNbr){
                while(m_teams.size() < teamNbr){ // tant que le nombre d'éléments
                                                 // contenus dans le tableau est
                                                 // inférieur au nombre d'équipes
                                                 // demandées
                    m_teams.push_back(Team{});   // on ajoute une équipe dans la collection
                }
            }

            Note au passage que je renommerais volontiers cette fonction en addTeams (ajouter "un certain nombre" d'équipe), et que je rajouterais volontiers une fonction addTeam (ajouter une seule équipe à la fois). Cela nous amènerait sans doute à une implémentation prenant une forme ressemblant à

            void Game::addTeam(){
               /* ajoute une et une seule équipe */
               m_team.emplace_back(Team{});
            }
            void Game::addTeams(size_t howMany){
                /* rajoute homMany equipes au jeu */
                for(size_t i = 0; i<howMany; ++i){
                    addTeam();
                }
            }

            La raison pour laquelle je te conseille de renommer ta fonction est que tu dois considérer ta classe Game comme... un fournisseur de services.

            En tant que tel, ta classe Game devra donc fournir deux grands types de services distincts:

            • répondre à des questions simples commecombien d'équipes y a-t-il? est que le jeu est en cours? quel est l'état de la classe (organisation des équipes, mise en place, en cours de jeu, ...) ou autres
            • obéir à aux ordres qu'on lui aura donné.

            Or, ta fonction teamCreator est -- typiquement -- un ordre que tu veux pouvoir donner à ta classe.  Et un ordre, c'est un verbe  à l'impératif (fait ceci, ajoute "N" équipes, supprimes les équipes, lance le jeu, ... ).

            De plus, le nom que l'on donne à une "fonctionnalité" (que ce soit une classe, une variable ou une fonction) doit -- autant que possible -- donner une idée très claire à  la personne qui lira le code après que tu l'aie écrit de ce à quoi la fonctionnalité va servir. L'idéal est donc que les fonctions qui vont permettre de donner des ordres soient identifiées à l'aide ... de verbes.

            teamCreator est un nom, et, de plus, cet identifiant ment au lecteur du code, car il va lui donner l'impression que c'est la classe Game qui va s'occuper effectivement de créer les équipes, alors que ce n'est clairement pas dans ses attributions: la classe Game doit gérer "la phase de jeu" dans son ensemble, mais, si il faut effectivement créer des équipes dans une phase de jeu, ce n'est pas à elle de le faire, même si elle devra faire appel à "quelque chose" capable de créer les équipes, ce que l'on va attendre de la part de la classe Game sera en fait ... d'ajouter les équipes.

            Enfin, si je te propose de le faire en deux fonctions différentes (ajoute "N" équipes et ajoute une équipe), c'est simplement parce que ce sont les deux manières dont l'utilisateur de la classe Game peut souhaiter l'utiliser:

            • Soit en mettant "lui-même" en place "toute la logique" qui lui permettra de créer le nombre d'équipes nécessaires (il ajoutera alors les équipes "une à une")
            • soit en demandant directement d'ajouter ... "un certain nombre" d'équipes (nombre qu'il aura déterminé d'une manière quelconque).
            • Partager sur Facebook
            • Partager sur Twitter
            Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
              12 août 2021 à 0:35:11

              OK j'ai capté les principes que tu m'as dit et je vais tenter de les appliquer. Du coup je fais comme ça pour créer mes équipes: 

              void Game::addTeams()
              {
                  cout << "We'll create " << m_teamsNbr << " teams..." << endl;
              
                  while(m_teams.size() < m_teamsNbr)
                  {
                      cout << "----- TEAM " << m_teams.size() + 1 << " -----" << endl;
                      Team newteam; 
                      newteam.askName();
                      newteam.askPlayerNbr();
                      m_teams.push_back(newteam);
                      cout << "Team " << m_teams.size() << " has been created. " << endl; 
                  }
              
              }

              Petit doute sur askName, ai-je le "droit" de modifier directement l'attribut de mon objet Team comme ceci: 

              void Team::askName()
              {
              	cout << "What's the name of your team? " << endl; 
              	cin >> m_name; 
              }



              • Partager sur Facebook
              • Partager sur Twitter
                12 août 2021 à 16:53:40

                Ben, en gros, tu dois te dire que c'est ton programme, que tu as donc tous les droits, pour autant que tu arrives à le faire comprendre au compilateur, et que ce soit permis par le langage ;)

                Par contre, il faut bien comprendre que certaines techniques sont "meilleures" que d'autres, dans le sens où elles vont te faciliter la vie "par la suite", et donc, qu'il sera sans doute plus intéressant d'utiliser ces techniques particulières plutôt que des techniques semblables mais ... "moins bonnes" :p

                Par exemple, l'idéal est toujours de faire en sorte que les comportements qui vont apporter "un certain nombre" de modifications puissent être qualifiés de "transactionnels".  C'est à dire que, soit toutes les modifications seront correctement effectuées et tu obtiens le résultat souhaité, soit aucune des modifications n'est prise en compte, les modifications qui avaient réussi sont, le cas échéant, annulées, et tu retrouve ton "système" exactement dans le même état que celui dans lequel il était avant de commencer à  appliquer les modifications.

                C'est d'ailleurs un principe que l'on va -- autant que possible -- essayer de respecter au pied de la lettre pour ce qui concerne la création de données.

                Par exemple, l'idéal, lorsque tu vas créer une équipe, c'est qu'elle soit directement utilisable, dans le sens où si tu t'attend à ce qu'une équipe "utilisable" puisse -- ad minima -- répondre aux questions "quel est ton nom?" et "combien de joueurs y a-t-il dans l'équipe?", hé bien, ce sont des informations que chaque équipe devrait être en mesure de donner dés le moment où elle aura été créée, et sans que la personne qui a décidé de créer l'équipe n'ait quoi que ce soit à faire après la création de l'équipe.

                C'est la raison pour laquelle on conseille généralement de créer les données le plus tard possible (juste avant que l'on ne se retrouve dans une situation dans laquelle on a vraiment besoin de la donnée en question), car cela nous donne généralement l'occasion de veiller à ce que l'on dispose de l'ensemble des informations qui devront être fournies pour la création de la donnée.

                Ainsi, au lieu d'avoir une logique qui

                • crée une équipe (sans nom, et dont on ignore le nombre de joueur)
                • modifie cette équipe pour lui donner un nom
                • modifie cette équipe pour lui indiquer le nombre de joueurs
                • ajoute la nouvelle équipe à l'instance de Game

                (tu remarqueras qu'il y a au moins deux étapes à effectuer après la création de l'équipe avant de pouvoir réellement l'utiliser, ce qui implique important d'en oublier au moins une :p )

                on préférera sans doute travailler dans l'autre sens, c'est à  dire:

                • récupérer (quelle qu'en soit la manière) le nom de l'équipe qui devra être créée
                • récupérer (quelle qu'en soit la manière) le nombre de joueurs dont l'équipe dispose
                • créer l'équipe en lui fournissant directement un nom et un nombre de joueurs
                • ajouter la nouvelle équipe à l'instance de Game

                (en veillant, bien sur, à  ne passer à l'étape suivante QUE si l'étape "en cours" a pu être menée à bien).

                Cette manière de travailler aura l'énorme avantage de nous faire atteindre "naturellement" les deux objectifs que je viens de citer, car

                • soit les quatre étapes citées se déroulent correctement et toutes les modifications permettant d'obtenir une nouvelle équipe sont prise en compte, soit, il n'y a "rien de mal fait", vu que le système reste exactement dans l'état dans lequel il était avant que l'on n'essaye de créer l'équipe
                • une fois créée, l'équipe est "directement utilisable" et il n'y a pas une "étape supplémentaire" à effectuer (au risque d'oublier de le faire) pour pouvoir l'utiliser

                Enfin, si j'ai un dernier conseil à te donner à l'occasion de cette intervention  ( de ce roman), c'est de toujours partir du principe que l'utilisateur est un "imbécile idiot", qui n'attend qu'une occasion de "faire une connerie".

                L'idéal est donc de ne JAMAIS faire confiance aux réponses ou aux demandes qui peuvent venir de la part de l'utilisateur, et donc de s'assurer de manière systématique que l'utilisateur

                • donne une réponse "correcte et cohérente" à la question qu'on lui pose (car il pourrait très bien te répondre 3 alors que tu lui demande le nom de l'équipe, ou, inversement, répondre "guilde des gentlemen cambrioleurs" alors que tu lui demande le nombre de joueurs)
                • fasse une demande "logique et cohérente" par rapport au contexte de la situation du moment même lors de l'utilisation (car il y aura forcément un moment où "il ne sera plus temps" d'accepter une nouvelle équipe, non ? )
                • Partager sur Facebook
                • Partager sur Twitter
                Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
                  12 août 2021 à 18:41:46

                  Ok c'est vrai que ça semble plus logique, je vais écouter tes conseils, merci!
                  • Partager sur Facebook
                  • Partager sur Twitter

                  nombre d'attributs variables dans une classe

                  × 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