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 :

Récupérer un objet se trouvant dans une autre fonction


Sujet :

C++

  1. #1
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Octobre 2010
    Messages
    2
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2010
    Messages : 2
    Points : 1
    Points
    1
    Par défaut Récupérer un objet se trouvant dans une autre fonction
    Bonjour, voilà mon problème. je m'entraîne avec les classes à faire un rpg en console. Mais je ne vois pas comment convertir l'objet de classe mère player en objet de la classe fille Guerrier via la fonction création, en sachant que le joueur aura le choix entre plusieurs classes. Merci d'avance .

    main.cpp
    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
    #include <iostream>
    #include <string>
     
    #include "Personnages.h"
    #include "Guerrier.h"
     
    using namespace std;
     
    Personnage Creation(Personnage &test);
     
    int main()
    {
        Personnage player;
     
        Creation(player);
     
        //Afficher l'état du joueur via la class fille
        player.Afficher();
     
        cout<< endl;
        system("pause");
     
        return 0;
    }
    Personne.h
    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
    #ifndef PERSONNE
    #define PERSONNE
     
    class Personnage
    {
          public:
                  Personnage(){};
                  Personnage(std::string c_name);
                  virtual ~Personnage(){}
     
     
          protected:
                  std::string nom;      
                  int hp, mp;
    };
     
    #endif
    Guerrier.h
    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
    #ifndef DEF_GUERRIER
    #define DEF_GUERRIER
     
    #include <string>
    #include <iostream>
    #include "Personnages.h"
     
    class Guerrier : public Personnage
    {
        public:
        Guerrier(){};
        Guerrier(std::string c_name,int c_pv);
        void Afficher();
        ~Guerrier(){};
    };
     
    #endif
    Fonction de création du personnage :
    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
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    Personnage Creation(Personnage &test)
    {
        bool validation = false;
        int choix = 0;
        string n_character;
     
        cout << "######################################" << endl;
        cout << "        CREATION DU PERSONNAGE" << endl;
        cout << "######################################" << endl << endl;
     
        while(!validation)
        {
            cout << "Choisissez le nom de votre personnage : " << endl;
            cin >> n_character;
            cout << endl;
     
            cout << "Choississez une classe :" << endl;
            cout << "1 : Guerrier" << endl;
            cout << "2 : Voleur" << endl;
            cout << "3 : Magicien" << endl;
            cin >> choix;
     
            cout << endl;
     
            switch(choix)
            {
                case 1:
                {
                    cout << "Vous avez choisi la classe guerrier" << endl<< endl;
                    Guerrier guerrier(n_character, 20);
                    Guerrier &perso = guerrier;
     
                    try
                    {
                        Personnage &perso1 = dynamic_cast<Personnage&>(perso);
                    }
                    catch (const std::exception& e)
                    {
                        std::cerr << e.what() << endl;
                    }
     
                    test = guerrier;
     
                    validation = true;
                    break;
                }
     
                case 2:
                {
                    cout << "Vous avez choisi la classe voleur" << endl;
                    break;
                }
     
                case 3:
                {
                    cout << "Vous avez choisi la classe magicien" << endl;
                    break;
                }    
            }
     
        }
        return test;
    }

  2. #2
    Membre éclairé
    Profil pro
    Inscrit en
    Décembre 2004
    Messages
    1 298
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2004
    Messages : 1 298
    Points : 886
    Points
    886
    Par défaut
    Salut, bienvenue sur le forum. Je ne vais pas vraiment répondre à ta question, mais en lisant ton code, j'ai plusieurs remarques à te faire :

    dans ta classe Personnage :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    Personnage(std::string c_name);
    est à remplacer par

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    Personnage(const std::string & c_name);
    Le & est très important car cela va t'éviter d'avoir des copies temporaires... Idem pour la classe Guerrier avec ton

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    Guerrier(std::string c_name,int c_pv);
    Dans cette même classe, tu as une fonction Afficher(). Surcharge l'opérateur <<. Il est fait pour ça.

    Dans le main, tu as écrit :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    Personnage Creation(Personnage &test);
    es-tu sûr de ne pas pouvoir mettre la fonction Creation comme une fonction membre de ta classe Personnage. Surtout, ne renvoie pas de Personnage car tu auras tous plein de copie temporaire. A la rigueur, renvoie un Personnage *, mais le mieux est d'avoir un constructeur par copie

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    Personnage::Personnage(const Personnage & test);
    De manière générale, je trouve que tu codes est plus dan l'esprit "C" que dans l'esprit "C++" où tu as des objets qui communiquent entre eux (et non de "simples" fonctions comme en C).

    Je n'ai pas répondu à ta question, je le sais, mais ce ne sont que qq remarques sur ton code.

  3. #3
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Octobre 2010
    Messages
    2
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2010
    Messages : 2
    Points : 1
    Points
    1
    Par défaut
    Merci pour tes conseils je vais faire les modifications, on m'a conseillé de voir tout ce qui tourne autours de l'object factory pour mon problème. Faut que je lise tout ça ^^.
    Pour es const & faut que je fasse ça pour le guerrier ? Même pour le int ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Guerrier(const std::string &c_name,const int &c_pv);

  4. #4
    Membre actif

    Profil pro
    Inscrit en
    Avril 2010
    Messages
    356
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 356
    Points : 206
    Points
    206
    Par défaut
    Ce serait une aberration de le faire pour les types primitifs : car, short, int, pointeurs, ...
    CAR :
    Une référence est un pointeur caché. Donc en réalité, tu fais une copie de ton pointeur quand tu passes par référence. La copie d'un pointeur étant aussi rapide que la copie d'un int, on peut négliger le temps de copie. Cependant, le temps de copie d'un guerrier est nettement superieur et donc, passer une référence est plus rapide.
    Par contre, passer un pointeur sur un entier revient à copier l'adresse de l'entier ce qui est aussi long que copier l'entier + les temps de récupération de l'adresse.
    Je n'ai pas du tout regardé ton problème.

  5. #5
    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,

    Il y a deux gros problèmes dans ton code:

    La fonction Creation renvoie un personnage sous forme de valeur, et non sous forme de référence ou de pointeur.

    Tu perds donc, de facto, toute possibilité d'utiliser le polymoprhisme

    Or, quellle que soit la "classe" du personnage choisi, avant d'être un guerrier, un mage ou un voleur, ce sera... un personnage, qui peut, en toute état de cause, effectuer un certain nombre d'actions " de base", éventuellement adaptées à sa classe:
    1. se déplacer
    2. attaquer
    3. se défendre
    4. ramasser des objets
    5. ...
    Il serait donc dommage d'en arriver à devoir tenter systématiquement la conversion du personnage en mage, en guerrier ou en voleur avant de pouvoir lui transmettre l'ordre d'entreprendre l'une ou l'autre de ces actions!!!

    De plus, il n'y a aucun intérêt à permettre la création d'un personnage "sans autre indication", car même les PNJ feront partie d'une classe (même s'il s'agit d'un mendiant que l'on a facile à ignorer pendant le jeu) et agiront en fonction de cette classe:

    Certains te demanderont une piece de monnaie chaque fois que tu passe près d'eux, d'autres t'attaqueront systématiquement, d'autres encore se défendront si tu les attaque mais accepteront de te parler, etc.

    Même si ce doit être un animal comme un loup qui t'attaque, il fera partie d'une classe particulière de personnage, avec ses caractéristiques propres

    L'idéal, pour éviter tout problème, est donc... de rendre ta classe personnage abstraite, afin d'éviter toute tentative d'instanciation d'un personnage sans que l'on sache exactement de quelle classe il fait partie

    Si tu prends déjà cette précaution élémentaire, par exemple, en déclarant le destructeur de Personnage virtuel pur (mais en l'implémentant néanmoins, car il sera nécessaire pour permettre la destruction de tous personnages émargeant à des classes particulières), le compilateur te fera remarquer que tu ne peux pas... renvoyer une instance de personnage par valeur.

    La solution est donc de renvoyer un pointeur ou une référence sur un objet de type Personnage, mais cela nous amène au deuxième problème:

    Tel que tu le fais actuellement, le personnage de type "guerrier" que tu crée est un... objet temporaire, dans le sens où il n'existe qu'entre le moment de sa création (la ligne Guerrier guerrier(n_character, 20); de la fonction Creation) et... la fin de la portée dans laquelle il est déclaré, à savoir: le break qui met fin aux instructions à effectuer pour créer un personnage de type guerrier

    Lorsque tu affecte à ta référence "test" l'objet de type guerrier, cela passe, parce que l'objet existe bel et bien, sauf qu'il est détruit... deux lignes plus tard (comme je l'ai dit: au niveau du break)

    A partir de là, toute tentative d'accès à test après ce break, et, partant de là, à la variable player de ta fonction main aura un comportement indéfini, car... l'objet aura été détruit

    Tu dois donc veiller à ce que le personnage créé ne soit pas détruit au moment où l'on va quitter la portée dans laquelle il est créé.

    Et, comme l'idée reste, malgré tout, de profiter du polymorphisme (de pouvoir profiter de n'importe quel personnage, quelle que soit sa classe réelle), nous avons énormément de choix d'implémentation:

    La plus rapide à mettre en oeuvre, mais sans doute la moins conseillée des possibilité consiste, simplement, à utiliser les pointeurs et pointeurs de pointeurs

    La fonction Creation prendrait la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    void Creation(Personnage ** test)
    {
        /* je passe l'affichage et les boucles ;-) */
        case 1:
            *test= new Guerrier(/* les paramètres */);
         break;
         case 2 : 
             *test = new Mage(/* les paramètres */);
         /*...*/
    }
    et la fonction main deviendrait proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    int main()
    {
        Personnage * perso;
        Creation(&perso); 
        /* la suite */
        /* il ne faudra pas oublier de libérer la mémoire allouée à perso quand
         * on n'en a plus besoin ;)
         */
    }
    voire, les référence sur pointeurs 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
    void Creation(Personnage *& test)
    {
        /* je passe l'affichage et les boucles ;-) */
        case 1:
            test= new Guerrier(/* les paramètres */);
         break;
         case 2 : 
             test = new Mage(/* les paramètres */);
         /*...*/
    }
    avec la fonction main associée:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    int main()
    {
        Personnage * perso;
        Creation(perso); 
        /* la suite */
        /* il ne faudra pas oublier de libérer la mémoire allouée à perso quand
         * on n'en a plus besoin ;)
         */
    }
    Si je dis que cette méthode est la moins conseillée, c'est parce que tu te retrouvera donc à manipuler des pointeurs "à tout va", et que c'est la porte ouverte à de nombreux problèmes (entre autres ceux qui liés au non respect de la constance avec les pointeurs, les risques de tentative de libération de la mémoire de manière inopportune ou encore les risques de fuites de mémoire si tu décide de réinvoquer Creation afin de jouer avec une autre classe de personnage sans avoir pris la peine de... détruire l'instance existante )

    De plus, cette manière de faire rend la fonction main responsable de la durée de vie du personnage, alors qu'elle ne devrait pas avoir cette responsabilité: elle a déjà bien assez à faire ainsi

    Une partie de la solution consiste à prévoir une classe qui... va s'occuper de gérer le personnage en cours.

    Elle pourrait prendre la forme 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
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    class CharacterManager
    {
        public:
            static Personnage & creation()
            {
                /* on demande ce qu'on veut comme personnage, son nom et
                 * tout ce qu'il est nécessaire de savoir
                 */
                if(current_!=NULL)
                {
                    /* s'il y a déjà un personnage courent, il est possible 
                     * d'envisager plusieurs politiques différentes, je prend
                     * ici le parti de lancer une exception
                     * (à définir par ailleurs ;))
                     */
                     throw CharacterExists();
                }
               switch (choix)
               {
                   case 1:
                       current_= new Guerrier(/* paramètres utiles */);
                       break;
                  case 2 : 
                        current_=new Mage(/* paramètres utiles */);
                        break;
                    /*...*/
                }
                return current_;   
            }
            /* on peut prévoir de détruire explicitement le personnage courent
             */
            static void Destroy()
            {
                delete current_;
                current_=0;
            }
            /* et, si on a décidé de lancer une exception lors de la création
             * d'un personnage s'il en existe déjà, la possibilité de changer
             * proprement de personnage
             */
             Personnage & change()
             {
                 Destroy();
                 return Creation();
             }
        private:
            static Personnage * current_;
    };
    /* sans oublier, Dans un fichier *.cpp unique
    #include <CharacterManager.hpp>
    Personnage *  CharacterManager::current=NULL;
    La fonction main deviendrait alors proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    int main()
    {
        Personnage & perso=CharacterManager::Creation();
        /* la logique du jeu */
     
        /* quand on veut quitter le jeu */
        CharacterManager::Destroy();
        return 0;
    }
    Et, comme je présume qu'il y aura sans doute quelques PNJ, il sera sans doute intéressant de faire en sorte que CharacterManager se charge... de les gérer, mais ca, c'est une autre histoire
    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

Discussions similaires

  1. Réponses: 2
    Dernier message: 11/03/2011, 21h17
  2. Réponses: 2
    Dernier message: 23/01/2011, 16h23
  3. récupérer les groupes se trouvant dans un autre domaine
    Par phoenix75 dans le forum VBScript
    Réponses: 0
    Dernier message: 23/10/2008, 12h18
  4. Réponses: 3
    Dernier message: 22/05/2006, 09h58

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