Classe objet cin for et string

Résolu
madhack Messages postés 23 Date d'inscription lundi 28 novembre 2016 Statut Membre Dernière intervention 27 avril 2017 - Modifié le 25 mars 2017 à 20:55
madhack Messages postés 23 Date d'inscription lundi 28 novembre 2016 Statut Membre Dernière intervention 27 avril 2017 - 4 avril 2017 à 17:37
Bonjour,
Me voici de retour pour la suite de ma formation après un bon mois d'absence.
J'ai "appris" la création d'une classe avec son principe d'encapsulation.
Je vais refaire plusieurs fois le projet du cours jusque à connaitre la syntaxe d'écriture par cœur.
Toutefois en fin de chapitre il est suggérer de développer un peu plus le code.
En donnant un nom à notre instance objet de classe Personnage.
J'ai essayer d'inclure l'attribut ou la variable membre m_nomPersonnage dans le constructeur.
Mais le compilateur m'as suggérer de le faire d'une autre manière ;).
Déjà voici le lien du code "cours" si ça intéresse : https://openclassrooms.com/uploads/fr/ftp/mateo21/cpp/rpg.zip
Et voici mon petit code de test pour cette histoire de demande de nom à l'utilisateur de la classe lors de la création de l'objet.
Le but étant ici que l'utilisateur indique le nombre de personnage à crée.
Puis entrer dans une boucle for afin de créer tous les objets un par un.

Personnage.hh
#ifndef PERSONNAGE_HH_INCLUDED
#define PERSONNAGE_HH_INCLUDED
#include <string>

class Personnage
{
public:
    Personnage(std::string nomPersonnage);
    void attaquer(Personnage &cible);

private:
    int m_vie;
    int m_mana;
    std::string m_nomPersonnage;

};


#endif // PERSONNAGE_HH_INCLUDED

Personnage.cpp
#include "Personnage.hh"
#include <string>

Personnage::Personnage(std::string nomPersonnage):m_mana(100),m_vie(100),m_nomPersonnage(nomPersonnage)
{

}

main.cpp
#include <iostream>
#include "Personnage.hh"

int main()
{
    int nbPersonnage;
    std::string nomPersonnage;
    std::cout<<"Veuillez indiquer le nombre de joueur dans la partie"<<std::endl;
    std::cin>>nbPersonnage;
    for (int i(0);i<nbPersonnage;i++)
    {
        std::cout<<"Veuillez indiquer le nom numéro : "<<i+1<<std::endl;
        std::cin>>nomPersonnage;
        Personnage nomPersonnage(nomPersonnage);
    }

return 0;
}

message dans la console(qui est une première pour moi)
et celui du compilateur
||=== Build: Debug in madrpg (compiler: GNU GCC Compiler) ===|
E:\projetc++\formation2017\openclassroom\POO\erxercicerpg\madrpg\Personnage.hh||In constructor 'Personnage::Personnage(std::string)':|
E:\projetc++\formation2017\openclassroom\POO\erxercicerpg\madrpg\Personnage.hh|13|warning: 'Personnage::m_mana' will be initialized after [-Wreorder]|
E:\projetc++\formation2017\openclassroom\POO\erxercicerpg\madrpg\Personnage.hh|12|warning: 'int Personnage::m_vie' [-Wreorder]|
E:\projetc++\formation2017\openclassroom\POO\erxercicerpg\madrpg\Personnage.ccp.cpp|4|warning: when initialized here [-Wreorder]|
||=== Build finished: 0 error(s), 3 warning(s) (0 minute(s), 0 second(s)) ===|
||=== Run: Debug in madrpg (compiler: GNU GCC Compiler) ===|

Voila je pense que ma variable nomPersonnage est un peu trop présente(genre omnisiante).
Je vais reprendre depuis le début mais si vous avez une soluce une voie une lumière suis à votre écoute.
Mad

8 réponses

madhack Messages postés 23 Date d'inscription lundi 28 novembre 2016 Statut Membre Dernière intervention 27 avril 2017
25 mars 2017 à 21:34
Peut ètre est il possible de mettre une commande dans le constructeur ?


Personnage::Personnage(std::string nomPersonnage)
{
std::cout<<"Veuillez lui donner un nom : "<<std::endl;
std::cin>>nomPersonnage;
}


Merci pour le temps que vous avez pris à me lire.
0
cptpingu Messages postés 3837 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 28 mars 2023 123
Modifié le 26 mars 2017 à 15:57
Bonjour.

On va revoir ensemble tous les problèmes rencontrées:
  • Warning du compilateur: quand on initialise des attributs, on le fait dans l'ordre *exact* ou ils sont déclarées. Inverse vie et mana dans ton initialisation, et ça ira mieux :)
  • Pour ton segfault, c'est dû au fait que tu nommes ton objet et ton nom de la même manière: nomPersonnage. Donc le compilateur croit que tu veux initialiser ton objet nomPersonnage avec lui même. Comme il n'existe pas, c'est comme si tu faisais Personnage nomPersonnage(0), ce qui fait un std::string(0) dans ton constructeur. Or un std::string(0) plante. De toute façon, il est mal nommé, puisque tu veux créer un personnage et non un nom de personnage. Donc ça devrait être: Personnage personnage(nomPersonnage).
  • Pour ton idée de demander le nom dans le constructeur, c'est techniquement faisable, mais très moche ! Ce n'est pas au constructeur de faire cela. On pourrait créer une méthode dédiée, mais pour le faire proprement, une surcharge d'opérateur serait pas mal (pas obligé néanmoins). Comme je ne veux pas te noyer sous des notions que tu n'as pas vu, on va garder cela pour plus tard.


Quelques conseils:
  • Donne un const std::string& au lieu d'un string, ce qui évite une copie pour rien (mais ignore ceci pour l'instant si ça te perturbe).
  • Préfère ++i à i++ (Ici ça ne change rien, pour plus tard ça te sera utile).
  • Je préfère _attribut plutôt que m_attribut, mais c'est une question de goût. Tu peux garder "m_", ce n'est pas gênant.


Une petite correction:
#include <iostream>

class Personnage
{
public:
  Personnage(const std::string& nomPersonnage);
  void attaquer(Personnage& cible);
  void print(std::ostream& out) const;

private:
  int _vie;
  int _mana;
  std::string _nomPersonnage;
};

Personnage::Personnage(const std::string& nomPersonnage)
  : _vie(100), _mana(100), _nomPersonnage(nomPersonnage)
{
}

void Personnage::print(std::ostream& out) const
{
  out << _nomPersonnage << ": vie=" <<_vie << ", mana=" << _mana;
}

int main()
{
  int nbPersonnage = 0;
  std::cout << "Veuillez indiquer le nombre de joueur dans la partie: ";
  std::cin >> nbPersonnage;

  for (int i = 0; i < nbPersonnage; ++i)
  {
    std::cout << "Veuillez indiquer le nom " << i + 1 << ":";
    std::string nomPersonnage;
    std::cin >> nomPersonnage;
    Personnage personnage(nomPersonnage);
    std::cout << "Création de: \n";
    personnage.print(std::cout);
    std::cout << std::endl;
  }

  return 0;
}


Améliorer votre expérience CodeS-SourceS avec ce plugin:
http://codes-sources.commentcamarche.net/forum/affich-10000111-plugin-better-cs-2#cptpingu-signature
0
madhack Messages postés 23 Date d'inscription lundi 28 novembre 2016 Statut Membre Dernière intervention 27 avril 2017
26 mars 2017 à 22:30
Bonsoir
Et merci pour la réponse toujours aussi rapide.
J'ai vu le principe de surcharge du constructeur ce que vous avez fait il me semble :


Personnage(const std::string& nomPersonnage);


Ou alors j'ai pas bien compris le principe de la surcharge dans une liste d'initialisation.
Pour ce qui est de :


Personnage::Personnage(const std::string& nomPersonnage)


Je vois le principe d'une référence constante pour éviter une utilisation de mémoire inutile.
Mais je dois avouer que dans la conception j'ai encore du mal a trouver les endroits ou cela doit se faire.
Cela dit je suppose que plus tard, il s'agira d'un automatisme enfin
je l’espère.
Et je crois qu'un moment donné cela passera par des pointeurs mais schuuuut, je ne suis pas du tout à l 'aise avec ces derniers pour l'instant.
Pour :


void Personnage::print(std::ostream& out) const
{
out << _nomPersonnage << ": vie=" <<_vie << ", mana=" << _mana;
}


je ne connais pas du tout le fonctionnement d'une variable ostream.
La méthode est intéressante si l'on veux afficher dans la console sans passer par std::cout.
Je devrais faire les recherches sur cette dernière.
Je ne savais pas pour l'ordre de déclaration et d'initialisation identique. Merci pour l'information.
Et merci pour les petits trick comme le ++i et le fait que l'on puisse commencer une variable par un _
Pour ce qui est du nomPersonnage, je vais encore refaire plusieurs fois l'exercice afin de bien comprendre les "tunnels" de liaison.
Surtout que l'exercice utilise encore une classe/objet Arme que l'on utilise dans un la classe/objet Personnage.
Je reviens vers vous dès que j'ai fini cette répétition/assimilation de
création initialisation utilisation de classe/objet.
Merci encore et à bientôt.
Mad
0
madhack Messages postés 23 Date d'inscription lundi 28 novembre 2016 Statut Membre Dernière intervention 27 avril 2017
4 avril 2017 à 16:05
Voila j'ai apporter les modifications que vous m'avez donné et cela marche à merveille.
Hormis le ostream&out
A ce sujet j'ai juste une dernière petite question sur ce post et après il passera en résolu.
Quel méthode ou source je dois utiliser pour avoir plus d'information sur cet variable.
idem pour la commande /n en fin de std::cout.
Je vais maintenant essayer de développer le programme avec plus d'interaction et méthodes ainsi que 1 ou 2 nouvelles classe associées.
En espérant vous lire bientôt.
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
cptpingu Messages postés 3837 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 28 mars 2023 123
Modifié le 4 avril 2017 à 16:24
Bonjour.

Les questions ne sont pas très claires :(. Je vais essayer d'y répondre (je risque de répondre à côté).

Quel méthode ou source je dois utiliser pour avoir plus d'information sur cet variable.

Si la question est: qu'est-ce qu'un std::ostream ?
Alors: C'est la classe de base de tous les flux.
Un buffer (std::ostringstream), une sortie standard (std::cout, std::cerr, std::clog), un fichier (std::ofstream) et même un socket réseau sont un type dérivé de std::ostream. Mettre un std::ostream veut dire qu'on accepte un flux quel qu'il soit. L'utilisateur nous fournira alors celui-ci.

Ex (voir code que je t'avais posté):
personnage.print(std::cout); // Sortie standard
personnage.print(std::cerr); // Sortie erreur
personnage.print(std::clog); // Sortie log
std::ostringstream buff;
buff << "Mon buffer: ";
personnage.print(buff); // Dans un buffer (une chaine de caractères si tu préfères)
std::cout << buff.str() << std::endl;

std::ostream file("toto.txt");
personnage.print(file); // Dans un fichier


Le but étant de laisse le choix à l'utilisateur où il veut mettre sa donnée.


idem pour la commande /n en fin de std::cout.

Si la question est: quand mettre \n et quand mettre std::endl ? Est-ce que c'est pareil ?

std::endl, c'est l'équivalent d'un \n + un std::flush. Tu peux très bien ne mettre que des \n ou que des std::endl (c'est pratiquement pareil). Personnellement, j'aime bien mettre de \n partout, et un std::endl tout à la fin.


Améliorer votre expérience CodeS-SourceS avec ce plugin:
http://codes-sources.commentcamarche.net/forum/affich-10000111-plugin-better-cs-2#cptpingu-signature
0
madhack Messages postés 23 Date d'inscription lundi 28 novembre 2016 Statut Membre Dernière intervention 27 avril 2017
4 avril 2017 à 17:02
hum d'accord merci.
Je pense que le ostream va encore me demander un peu de temps.
Même si j'utilise déjà quelques classe "enfants" ou "héritières"
comme le std::cout ou ofstream(encore que celui ci j'ai du mal pour l'instant).
Ensuite dans mon cas de figure j'assimile l'utilisateur au "joueur".
Donc je voudrais verrouiller ces informations avec ma méthode.
Il s'agira peut être d'un autre poste vue que je veux essayer sauvegarder les nomJoueurs dans un vector pour pouvoir les réutiliser.
Mais oui il serais bon d'envisager que l'utilisateur est un programmeur.
La question de base était de savoir si pour le ostream j'aurai du faire une recherche google ou si il y a un wiki de référence.
Afin de ne pas tout demander et essayer de trouver des réponses de manière autonome.
Merci beaucoup pour ces lumières et les futures ;)
0
cptpingu Messages postés 3837 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 28 mars 2023 123
Modifié le 4 avril 2017 à 17:24
Quand tu codes une classe, une bibliothèque ou une méthode/fonction, tu t'adresses à programmeur (toi, un collègue ou un inconnu qui reprendrait ton code). Reprendre ton code est plus agréable si celui-ci est "propre" et "bien pensé".
Il faut bien séparer le code "générique" (réutilisable), du code "spécifique" (qui réutilise le code générique pour créer une fonctionnalité métier bien particulière).
La part de code "générique" dans un "bon" code est généralemenet très supérieur à la part de code "spécifique". Un code bien pensé est aisément réutilisable.
Généralement, on ne met jamais un flux en "dur" dans une classe. On le laisse sélectionnable. Si tu as du std::cout dans une classe ou méthode réutilisable, c'est qu'il y a 90% de chance qu'il y ait un problème de conception :).

Pour référence, j'utilise ce site: http://www.cplusplus.com/reference/
Exemple pour std::ostream: http://www.cplusplus.com/reference/ostream/ostream/?kw=ostream (pas le meilleur exemple)
Un exemple plus intéressant: http://www.cplusplus.com/reference/string/string/?kw=string , on peut inspecter la méthode substr (et on tombe sur de la doc avec exemple): http://www.cplusplus.com/reference/string/string/substr/

Ce site n'est pas parfait (c'est juste de la doc C++), mais c'est très pratique. Une recherche dans un moteur de recherche fonctionne pas mal aussi :)


Améliorer votre expérience CodeS-SourceS avec ce plugin:
http://codes-sources.commentcamarche.net/forum/affich-10000111-plugin-better-cs-2#cptpingu-signature
0
madhack Messages postés 23 Date d'inscription lundi 28 novembre 2016 Statut Membre Dernière intervention 27 avril 2017
Modifié le 4 avril 2017 à 17:38
Ok encore merci.
Oui c'est sur il faut lors de la conception bien pensé son code.
Pour m'as part je vais déjà apprendre à codé ^^.
Un grand merci je vais mettre en favoris tes liens de mon coté j'ai trouvé
https://fr.wikibooks.org/wiki/Programmation_C%2B%2B
plus précisément :
https://fr.wikibooks.org/wiki/Programmation_C%2B%2B/Les_entr%C3%A9es-sorties
Allez zou je passe en résolu.
Encore un grand merci et à bientôt.
0
Rejoignez-nous