Forum |  HardWare.fr | News | Articles | PC | S'identifier | S'inscrire | Shop Recherche
1473 connectés 

  FORUM HardWare.fr
  Programmation
  C++

  problème avec le constructeur de copie

 


 Mot :   Pseudo :  
 
Bas de page
Auteur Sujet :

problème avec le constructeur de copie

n°2053457
razuki
Posté le 01-02-2011 à 16:01:11  profilanswer
 

Bonjour,
 
J'ai défini un classe Personnage. J'essaie de copier le Personnage goliath vers le Personnage david comme suit :
 

Code :
  1. int main()
  2. {
  3. Personnage goliath("Epée aiguisée", 20);
  4. Personnage david = goliath;
  5. ...
  6. }


 
Voici les constructeurs que j'ai crée dans la classe Personnage :

Code :
  1. Personnage::Personnage() : m_vie(100), m_mana(100)
  2. {
  3. m_arme = new Arme();
  4. }
  5. Personnage::Personnage(string nomArme, int degatsArme) : m_vie(100), m_mana(100)
  6. {
  7. m_arme = new Arme(nomArme, degatsArme);
  8. }


 
Le compilateur me dit alors :
 
Undefined reference to 'Personnage::Personage(Personnage const& )
 
Ma question :
-- Le compilateur ne crée - t - il pas automatiquement le constructeur de copie ?
-- le problème serait - il ailleurs ?
 
Merci d'avance pour votre aide.


Message édité par razuki le 01-02-2011 à 17:51:52
mood
Publicité
Posté le 01-02-2011 à 16:01:11  profilanswer
 

n°2053459
mr simon
Posté le 01-02-2011 à 16:32:49  profilanswer
 

Dans le code que tu as poste, il y a deux fois le meme constructeur. J'imagine que tu as un constructeur par defaut et un constructeur prenant deux parametres.
 
Le compilateur devrait creer un constructeur de recopie par defaut ... Mais dans ton cas, il ne sera pas bon car, les pointeurs m_arme "pointront" vers la meme "arme" (le constructeur par defaut se contentant de copier coller les valeurs des membres de l'objet copie). Il faut donc que tu definisses un constructeur de recopie faisant une copie correct des pointeurs (donc au moins de m_arme).
 

n°2053470
razuki
Posté le 01-02-2011 à 17:34:49  profilanswer
 

Le problème est résolu. En fait j'ai déclaré le constructeur de copie mais je ne l'ai pas défini ...
Sinon un autre problème toujours lié au constructeur de copie ( et donc tu as parlé un peu  dans ta réponse ) :
 
-- j'ai deux Personnage david et goliath qui ont un pointeur arme pointant vers le meme objet arme (voir main ci-dessus).
-- j'ai détruit david ( d'abord son arme, puis lui meme ), puis j'ai détruit goliath et ceux en fin de main( )
 

Code :
  1. goliath.~Personnage();
  2. david.~Personnage();


 
Ma question :
est-ce normal que le compilateur ne rale pas, que le programme ne plante pas ... ?
En effet quand je détruit goliath l'arme disparait avec lui. Quand je tente de détruire david, le compilateur est censé cherche son arme ... NON ?
 
Merci d'avance pour votre aide.  

n°2053473
shaoyin
Posté le 01-02-2011 à 17:43:14  profilanswer
 

razuki a écrit :

Code :
  1. goliath.~Personnage();
  2. david.~Personnage();



 
 :heink:  
 
Comment supprimes-tu des objets, toi ? "delete", ca te parle ?
 
Poste un peu le code de ton destructeur, pour voir.

n°2053477
razuki
Posté le 01-02-2011 à 17:50:26  profilanswer
 

Code :
  1. Personnage::~Personnage()
  2. {
  3. delete m_arme;
  4. }


Message édité par razuki le 01-02-2011 à 17:50:39
n°2053482
shaoyin
Posté le 01-02-2011 à 18:26:14  profilanswer
 

Pour l'instant, je ne vois pas d'où vient ce prodige, mais j'ai deux remarques à faire :
 
1) On n'appelle JAMAIS un destructeur directement. Il faut toujours passer par "delete". Je parierai que si tu remplace tes deux horribles appels aux destructeurs par des "delete" en bonne et due forme, ton programme va se planter comme tu t'y attends.
 
2) L'opérateur "delete" fait deux choses : il invoque le destructeur de l'objet ET il libère la mémoire occupée par cet objet.  
 
Si dans ta fonction ~Personnage() tu avais appelé m_arme->~Arme() au lieu de faire un "delete m_arme", l'espace mémoire occupé par m_arme n'aurait pas été libéré, donc ca aurrait pu expliquer pourquoi ca ne posait pas de souci de "détruire" cette ressource commune.


Message édité par shaoyin le 01-02-2011 à 18:26:48
n°2053511
xilebo
noone
Posté le 01-02-2011 à 21:20:17  profilanswer
 

Dans ton constructeur par copie, ce n'est pas l'adresse de "arme" que tu dois dupliquer, mais l'objet "arme". Chaque objet Personnage doit avoir sa propre "arme".  
 

n°2053555
razuki
Posté le 02-02-2011 à 00:42:40  profilanswer
 

J'ai vu quelque part que le compilateur fait appel implicitement au destructeur. C'est ce que shaoyin voulait me dire ppeut etre dans son 1). J'ai aussi essayé de ne pas mettre  les deux lignes ci-dessous au début, mais le compilateur ne rale pas non plus. Je m'attendais à ce qu'il rale.  
1. goliath.~Personnage();
2. david.~Personnage();

n°2053556
razuki
Posté le 02-02-2011 à 00:54:48  profilanswer
 

Si j'ai bien compris :
1) le compilateur (?) détruit implicitement systématiquement david et goliath avant de terminer le programme.
2) Je n'ai donc pas besoin de faire appel au destructeur.
 
david utilise une arme A, goliath utilise la meme arme A. Les deux pointent sur le meme objet A.
-- le compilateur (?) détruit implicitement systematiquement david ET donc son arme => OK
-- le compilateur (?) détruit implicitement systematiquement goliath MAIS il ne trouve pas l'arme de celui-ci car l'arme a été détruite avec david ...
 
Pourquoi le compilateur ne rale pas ?

n°2053557
mr simon
Posté le 02-02-2011 à 01:13:10  profilanswer
 

razuki a écrit :

Si j'ai bien compris :
1) le compilateur (?) détruit implicitement systématiquement david et goliath avant de terminer le programme.
2) Je n'ai donc pas besoin de faire appel au destructeur.


 
1) Oui chaque variable est detruite lorsqu'elle sort du scope:

Code :
  1. int main() {
  2.  Personnage a;
  3.  {
  4.      Personnage b;
  5.  } // destruction de "b"
  6. } // destruction de "a"


 
2) on n'appelle jamais un destructeur manuellement. Par contre, tu DOIS utiliser "delete" pour liberer memoire allouee par "new", celui-ci se chargera d'appeler le destructeur (ou les destructeurs en cas d'heritage)
 
Ensuite le compilateur en tant que tel ne te dira jamais rien sur ce genre de probleme (ca serait trop facile ;)), c'est a l'execution que ca plantera (memoire corrompue). Ce code devrait planter:

Code :
  1. int main(void)
  2. {
  3.     Personnage a("epee", 20);
  4.     Personnage b = a;
  5.     return 0;
  6. }


Message édité par mr simon le 02-02-2011 à 01:13:27
mood
Publicité
Posté le 02-02-2011 à 01:13:10  profilanswer
 

n°2053594
hephaestos
Sanctis Recorda, Sanctis deus.
Posté le 02-02-2011 à 09:56:19  profilanswer
 

xilebo a écrit :

Dans ton constructeur par copie, ce n'est pas l'adresse de "arme" que tu dois dupliquer, mais l'objet "arme". Chaque objet Personnage doit avoir sa propre "arme".  
 


Pas forcément, s'il y a beaucoup de personnages et une banque de données d'arme, et que chaque personnage ne modifie pas sa propre arme il aurait meilleur compte à implémenter une classe de banque d'armes qui instancie les armes utilisées sur demande, et à fournir à chaque personnage une fonction donnant accès à une référence vers une arme constante :

Code :
  1. const Arme& getArme();

n°2053595
hephaestos
Sanctis Recorda, Sanctis deus.
Posté le 02-02-2011 à 09:56:56  profilanswer
 

Dans tous les cas, les pointeurs bruts c'est la porte ouverte à toutes les fenêtres, c'est pas la la solution ici.

n°2053624
esox_ch
Posté le 02-02-2011 à 10:50:23  profilanswer
 

hephaestos a écrit :

Dans tous les cas, les pointeurs bruts c'est la porte ouverte à toutes les fenêtres, c'est pas la la solution ici.


:lol:


---------------
Si la vérité est découverte par quelqu'un d'autre,elle perd toujours un peu d'attrait
n°2053651
razuki
Posté le 02-02-2011 à 11:59:09  profilanswer
 

Bonjour,  
Je débute en C++ et j'aimerais provoquer des erreurs justement à l'exécution pour voir comment ca fonctionne.
 
 
Voici mon code Personnage.h :

Code :
  1. class Personnage
  2. {
  3.     public:
  4. Personnage();
  5. Personnage(std::string nomArme, int degatsArme);
  6. Personnage(const Personnage &personnageACopier);
  7.     void recevoirDegats(int nbDegats);
  8.     void attaquer(Personnage &cible);
  9.     void boirePotionDeVie(int quantitePotion);
  10.     void changerArme(std::string nomNouvelleArme, int degatsNouvelleArme);
  11.     bool estVivant();
  12.     void afficherEtat();
  13.     ~Personnage();
  14.     private:
  15.     int m_vie;
  16.     int m_mana;
  17. Arme* m_arme;
  18.     //std::string m_nomArme; // Pas de using namespace std, donc il faut mettre std:: devant string.
  19.     //int m_degatsArme;
  20. };


Voici mon code Personnage.cpp :

Code :
  1. #include <iostream>
  2. #include <string>
  3. #include "Personnage.h"
  4. using namespace std;
  5. //Personnage::Personnage() : m_vie(100), m_mana(100), m_nomArme("Epée rouillée" ), m_degatsArme(10) {}
  6. Personnage::Personnage() : m_vie(100), m_mana(100)
  7. {
  8. m_arme = new Arme();
  9. }
  10. //Personnage::Personnage(string nomArme, int degatsArme) : m_vie(100), m_mana(100), m_arme(nomArme, degatsArme) {}
  11. Personnage::Personnage(string nomArme, int degatsArme) : m_vie(100), m_mana(100)
  12. {
  13. m_arme = new Arme(nomArme, degatsArme);
  14. }
  15. Personnage::Personnage(const Personnage &personnageACopier)
  16. {
  17.     m_vie = personnageACopier.m_vie;
  18.     m_mana = personnageACopier.m_mana;
  19.     m_arme = personnageACopier.m_arme;
  20.     //m_arme = new Arme(*(personnageACopier.m_arme));
  21. }
  22. Personnage::~Personnage()
  23. {
  24. delete m_arme;
  25. }
  26. void Personnage::recevoirDegats(int nbDegats)
  27.     {
  28.  m_vie -= nbDegats;
  29.  if (m_vie < 0)
  30.   m_vie = 0;
  31.     }
  32.  void Personnage::attaquer(Personnage &cible)
  33.  {
  34.   cible.recevoirDegats(m_arme->getDegats());
  35.  }
  36.     void Personnage::boirePotionDeVie(int quantitePotion)
  37.     {
  38.      m_vie += quantitePotion;
  39.      if (m_vie > 100)
  40.       m_vie = 100;
  41.     }
  42.     void Personnage::changerArme(std::string nomNouvelleArme, int degatsNouvelleArme)
  43.     {
  44.      m_arme->changer(nomNouvelleArme, degatsNouvelleArme);
  45.     }
  46.     bool Personnage::estVivant()
  47.     {
  48.      if (m_vie > 0)
  49.       return true;
  50.      else
  51.    return false;
  52.     }
  53.     void Personnage::afficherEtat()
  54.     {
  55.         cout << "Vie : " << m_vie << endl;
  56.         cout << "Mana : " << m_mana << endl;
  57.         m_arme->afficher();
  58.     }


 
Mon main :
 

Code :
  1. int main()
  2. {
  3.     // Création des personnages
  4.     //Personnage david, goliath("Epée aiguisée", 20);
  5. Personnage goliath("Epée aiguisée", 20);
  6. Personnage david = goliath;
  7.     // Au combat !
  8.     goliath.attaquer(david);
  9.     david.boirePotionDeVie(20);
  10.     goliath.attaquer(david);
  11.     david.attaquer(goliath);
  12.     goliath.changerArme("Double hache tranchante vénéneuse de la mort", 40);
  13.     goliath.attaquer(david);
  14.     // Temps mort ! Voyons voir la vie de chacun...
  15.     cout << "David" << endl;
  16.     david.afficherEtat();
  17.     cout << endl << "Goliath" << endl;
  18.     goliath.afficherEtat();
  19.     //goliath.~Personnage();
  20.     //david.~Personnage();
  21.     return 0;
  22. }


 
le résultat après execution :

Code :
  1. David
  2. Vie : 40
  3. Mana : 100
  4. Arme : Epée aiguisée (Dégâts : 20)
  5. Goliath
  6. Vie : 80
  7. Mana : 100
  8. Arme : Double hache tranchante vénéneuse de la mort (Dégâts : 40)


 
Donc là le programme ne plante pas, contrairement à quoi je m'attendais.


Message édité par razuki le 02-02-2011 à 12:16:42
n°2053672
shaoyin
Posté le 02-02-2011 à 13:53:24  profilanswer
 

Hum... d'un côté, ton programme est en train de se terminer lorsque se passe la double destruction du champ "m_arme". Alors le plantage n'est peut-être pas vraiment mis en évidence.
 
Tu peux essayer de mettre tout le contenu de ta fonction "main" (sauf le 'return 0;') dans un bloc, et de faire d'autres choses après ce bloc (des écritures sur std::cout, par exemple).  

n°2053680
hephaestos
Sanctis Recorda, Sanctis deus.
Posté le 02-02-2011 à 14:06:15  profilanswer
 

C'est bizarre que tu te retrouves avec deux armes différentes alors qu'effectivement ton constructeur de copie garde le même pointeur...

n°2053682
shaoyin
Posté le 02-02-2011 à 14:08:06  profilanswer
 

J'avais pas fait gaffe à ca...


Message édité par shaoyin le 02-02-2011 à 14:09:31
n°2053683
hephaestos
Sanctis Recorda, Sanctis deus.
Posté le 02-02-2011 à 14:08:59  profilanswer
 

Oui, et il devrait aussi changer l'arme de David quand il change l'arme de Goliath, et ce n'est pas le cas.


Message édité par hephaestos le 02-02-2011 à 14:09:05
n°2053687
shaoyin
Posté le 02-02-2011 à 14:14:51  profilanswer
 

razuki, tu peux poster le code de ta classe "Arme", stp ?

n°2053703
razuki
Posté le 02-02-2011 à 14:34:37  profilanswer
 

shaoyin > j'ai regroupé toutes les instructions dans main( ) ( sauf return 0 ) dans un bloc. Puis j'ai rajouté un cout << "test"  à l'extérieur du bloc. Effectivement il n'affiche pas "test" quand David et Goliath possèdent le meme objet arme. Par contre, "test" s'affiche bien quand les deux personnages possède deux objets armes différents.
 
Hephaestos> moi non plus j'ai pas remarqué qu'il n'a pas changé l'arme de David ...
 
Je vais voir plutard et je vous tiendrai au courant.

n°2053713
mr simon
Posté le 02-02-2011 à 15:04:09  profilanswer
 

Je serais bien curieux de voir le code de la classe Arme, car avec le code que tu as poste, je ne vois pas comment David peut garder son arme (je ne peux meme pas imaginer comment realiser cela avec le code du constructeur de recopie que tu as).
 
Es tu sur que tu as poster le bon code pour Personnage, car si tu as :

Code :
  1. m_arme = new Arme(*(personnageACopier.m_arme));


(qui est en commentaire), ca marchera.
 
PS1: Pour les arguments de type std::string, utiliser une reference constante (i.e const std::string& ) est presque toujours une bonne idee!
PS2: Rendre les methodes constantes (par exemple afficherEtat()) est aussi une bonne pratique

n°2053720
razuki
Posté le 02-02-2011 à 15:14:42  profilanswer
 

.h
 

Code :
  1. class Arme
  2. {
  3.     public:
  4.     Arme();
  5.     Arme(std::string nom, int degats);
  6.     void changer(std::string nom, int degats);
  7.     void afficher();
  8.     int getDegats() const;
  9.     //string setNomArme() const;
  10.     private:
  11.     std::string m_nom;
  12.     int m_degats;
  13. };
  14. #endif


 
.cpp  
 

Code :
  1. Arme::Arme() : m_nom("Epée rouillée" ), m_degats(10)
  2. {
  3. }
  4. Arme::Arme(string nom, int degats) : m_nom(nom), m_degats(degats)
  5. {
  6. }
  7. void Arme::changer(string nom, int degats)
  8. {
  9.     m_nom = nom;
  10.     m_degats = degats;
  11. }
  12. void Arme::afficher()
  13. {
  14.     cout << "Arme : " << m_nom << " (Dégâts : " << m_degats << " )" << endl;
  15. }
  16. int Arme::getDegats() const
  17. {
  18.     return m_degats;
  19. }


n°2053736
mr simon
Posté le 02-02-2011 à 15:36:35  profilanswer
 

Rien de bien extraordinaire la dedans.
 
Es-tu sur de ta sortie? car ca n'a pas de sens pour moi ...

n°2053743
shaoyin
Posté le 02-02-2011 à 15:48:14  profilanswer
 

Moi aussi, je sèche complètement sur ce problème...

mood
Publicité
Posté le   profilanswer
 


Aller à :
Ajouter une réponse
  FORUM HardWare.fr
  Programmation
  C++

  problème avec le constructeur de copie

 

Sujets relatifs
Problème de compilation avec Code Blockprobleme de comparaison
[VBA Excel] Problème ComboBox et Function [Résolu]Problème scrollbar page perso
probleme d'exécutionProblème de connexion malgré les bons identifiants
Probleme verification champ formulaire javascriptA supprimer
Problème d'affiche en PHP[MAC]Probleme html et swf
Plus de sujets relatifs à : problème avec le constructeur de copie


Copyright © 1997-2022 Hardware.fr SARL (Signaler un contenu illicite / Données personnelles) / Groupe LDLC / Shop HFR