Partage
  • Partager sur Facebook
  • Partager sur Twitter

Création d'un objet

Sujet résolu
    12 avril 2021 à 18:13:13

    Bonjour ! Je débute en c++, et je me demande comment faire pour créer un un objet d'un classe. j'ai fait le code suivant

    Main.cpp

    #include <iostream>
    #include "humain.h"
    #include "humain.cpp"
    
    
    
    
    int main() {
    
    	humain h1 = humain ("Frederic",18,180,true);
    	//humain h2;
    	
    	std::cin.ignore();
    	return 0;
    }

    humain.h

    #include <string>
    #ifndef MON_FICHIER_HPP
    #define MON_FICHIER_HPP
    
    class humain {
    	//Champs - Attributs
    public:
    	std::string name;
    	int age;
    	int taille;
    	bool sportif;
    
    	//Constructeur
    	humain(std::string name, int age, int taille, bool sportif) {
    		this->name = name;
    		this->age = age;
    		this->taille = taille;
    		this->sportif = sportif;
    	}
    
    	//Getters&&Setters
    	std::string getName() const;
    	void setName(std::string newName);
    	bool isSportif() const;
    	void setSportif(bool newSportif);
    	int getAge() const;
    	void setAge(int newAge);
    	int getTaille() const;
    	void setTaille(int newTaille);
    
    };
    
    
    #endif
    

    humain.cpp

    #include <iostream>
    #include <string>
    #include "humain.h"
    
    
    void humain::setName(std::string newName)
    {
    	this->name = newName;
    }
    
    std::string humain::getName() const
    {
    	return this->name;
    }
    
    bool humain::isSportif() const
    {
    	return this->sportif;
    }
    
    void humain::setSportif(bool newSportif)
    {
    	this->sportif = newSportif;
    }
    
    int humain::getAge() const
    {
    	return this->age;
    }
    
    void humain::setAge(int newAge)
    {
    	this->age = newAge;
    }
    
    int humain::getTaille() const
    {
    	return this->taille;
    }
    
    void humain::setTaille(int newTaille)
    {
    	this->taille = newTaille;
    }
    

    Quand je crée mon objet j'obtiens une erreur

    GravitéCodeDescriptionProjetFichierLigneÉtat de la suppression

    ErreurC2440'initialisation' : impossible de convertir de 'humain *' en 'humain'Project1D:\C++\Project1\Main.cpp10

    J'ai vraiment besoin d'aide svp :) Si jamais vous voyez d'autres erreurs dites le moi plz ^^




    -
    Edité par alpacinonogeek 12 avril 2021 à 18:19:57

    • Partager sur Facebook
    • Partager sur Twitter
      12 avril 2021 à 18:49:05

      >J'ai vraiment besoin d'aide svp :) Si jamais vous voyez d'autres erreurs dites le moi plz ^^

      Bon, on va pas aller par quatre chemins, ce code est une horreur.

      Cela n'a rien à voir avec vous mais on voit clairement que vous suivez ou avez suivi cette calamité qu'est le cours de C++ d'OpenClassroom.

      Vous êtes bon pour tous désapprendre de ce que cours maléfique vous a mis insidieusement dans votre crane et de lire des sources de "qualité" comme le cours de zeste de savoir (c'est un peu restreint en terme de source francophone de qualité).

      Vous expliquez votre erreur, c'est comme vous expliquer comment débloquer la culasse de votre fusil pour que vous vous tiriez une cartouche de 12 dans le pied juste après. Alors, à moins d'être masochiste ...

      -
      Edité par bacelar 12 avril 2021 à 18:49:48

      • Partager sur Facebook
      • Partager sur Twitter
      Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.
        12 avril 2021 à 18:58:57

        Salut, je pense que l'on ne peut pas résoudre ton erreur "directement".

        Je m'explique : j'ai réuni ici ton code en un seul fichier qui compile parfaitement (testé avec x86-64 gcc 10.3).

        Tu as quelques soucis de conception :

        • n'inclus pas de .cpp, mais seulement des .h/.hpp, les headers contiennent des déclarations de fonctions / classes (il existe une classe humain qui possède une procédure (fonction qui retourne void) 'setName' prenant en paramètre un std::string), alors que les sources apportent les implémentations (codage du comportement de la fonction, dans l'exemple c'est this->name = newName)
        • ne mets pas les attributs en public, ça ne respecte absolument pas l'encapsulation (une classe possède des données qu'elle cache à l'utilisateur pour éviter que cet imbécile y touche, mais propose une interface (des fonctions publiques) qui remplit le rôle de la classe (SRP je crois, renseigne toi sur SOLID) correctement)
        • juste pourquoi tu implémentes le constructeur dans le header ? C'est une fonction comme une autre, tu peux juste la déclarer dans humain.h et l'implémenter dans umain.cpp
        • pour différencier tes attributs de tes paramètres de méthodes, tu devrais utiliser une notation (il existe m_attribut ou attribut_, mais pas _attribut qui pourrait entrer en conflit avec la STL)
        • initialise tes attributs par défaut, comme ceci :
        class humain {
            // par défaut private
            std::string name_{};
            int age_{};
            int taille_{};
            bool sportif_{};
        };
        • std::string est un type composé (non builtin comme char ou int) et donc qui peux prendre de la place (imagine des milliers voire des millions de carctères), passe le en référence constante dans tes paramètres [type fonction(const std::string& str)], mais renvoie ben par valeur (référence sur variable locale détruite = adresse invalide = plantage (segmentation fault ?))
        • bon, là c'est perso, mais tu peux mettre age_ et taille_ en unsigned au lieu de int, car tu ne peux ni mesurer -50cm ni être agé de -9ans*

        @bacelar Quand même soit sympa, il a pas osé mettre des pointeurs nus ou tableaux C comme nous l'apprend si bien le "fameux" cours d'OC :lol:

        -
        Edité par Chi_Iroh 12 avril 2021 à 19:02:41

        • Partager sur Facebook
        • Partager sur Twitter
          12 avril 2021 à 18:59:12

          Mise à part ça,

          ton code fonctionne, l'objet h1 est bien créé !

          #include "humain.cpp"

          On n'inclus pas les fichiers sources .cpp  On les compiles séparément et on lie le fichier objet pour produire l'exécutable !

          Dans tes fonctions membres, tu n'es pas obligé d’accéder aux donnés membre via le pointeur this, tu peux y accéder directement. 

          • Partager sur Facebook
          • Partager sur Twitter
            12 avril 2021 à 19:15:49

            Autre point à dire, est que ta classe est une véritable passoire, je m'explique:
            A quoi peuvent bien servir les membres privé si pour chacun d'entre eux, il y a un getter et un setter ?
            Autant les mettre en public, ca t'évitera d'écrire du code inutile.

            Ensuite, elle n'a pas de sens.
            Si en effet, un humain peut devenir sportif a un moment de sa vie, et ne plus l'être a un autre moment, voir changer de nom (mafioso repentis ?), par contre changer de taille, d'âge ne se fait pas à volonté.
            Il sera plus logique d'ajouter une fonction membre grandit(), ou vieillit() qui ajustera la taille et l'âge en conséquence.

            Utilise également des types appropriés, une taille, un âge ne peuvent être négatif.

            Enfin (mais pour un débutant, c'est probablement trop avancé), ta classe à une sémantique d'entité, par conséquent elle ne doit être ni copiable, ni assignable.

            • Partager sur Facebook
            • Partager sur Twitter
              12 avril 2021 à 19:26:26

              @Deedolith De plus, les membres sont déclarés public ! Autrement dit, ses getters / setters ne servent vraiment à rien...

              Et juste comme ça, autoriser déplacement, c'est cohérent dans ce contexte non ?

              (Parce que les unique_ptr le font bien)

              -
              Edité par Chi_Iroh 12 avril 2021 à 19:28:27

              • Partager sur Facebook
              • Partager sur Twitter
                12 avril 2021 à 20:14:07

                Wouah y a beaucoup de réponses je sais pas par ou commencer… Alors premièrement, il faut que vous sachiez que me critiquer ou me rabaisser n'arrangera rien. Comme je l'ai dit, je commence C++ et j'apprends via des tuto. Mais comment faire pour apprendre un langage si dans la majorité des vidéo, les personnes font les choses différemment ? Ensuite, la logique des mes classes setters et getters ne sont pas très importante. Je m'explique, le but de mon code est de faire un objet h1 et h2 humain et de pouvoir les modifier et récupérer leurs valeurs après modification. Passons le coté logique "un humain ne change pas d'âge comme ça ! Ni sa taille !" Je m'en fou ! Je veux faire des getters et setters afin de modifier les attributs.

                Ensuite merci à ceux qui ont joué le jeu et répondu pour m'aider. @rouloude sache que effectivement, pour une raison que j'ignore, il y a l'inclusion de humain.cpp, j'ai du cliquer sur l'une des correction proposé par Visual studio sans voir merci beaucoup :) et enfin, c'est vrai que le code fonctionne. Je sais pas pourquoi il me marquait une erreur avant. Donc je suppose que le sujet est résolu ??? 

                • Partager sur Facebook
                • Partager sur Twitter
                  12 avril 2021 à 20:17:30

                  +1 à pas besoin de rabaisser les débutants qui arrivent mal chaussés.

                  @al' Les ressources que tu utilises pour apprendre le C++ sont plus que perfectibles. Je ne saurais dire si c'est le cours local ou un cours de Java (indices: les this-> et les getters dans tous les sens) porté en C++. Il y a des choses à redire.

                  Le premier point qui t'a été signalé, c'est les inclusions. On compile les .cpp, et pour pouvoir être compilés, toute chose qui y est utilisée doit être déclarée une et une seule fois, voire définie (cas des types). Vu que l'on n'a pas envie de dupliquer déclarations de fonctions & variables et définitions de types à l'identique un peu partout, parce que l'on sait qu'il n'y a rien de tel que de se répéter pour faire des erreurs idiotes, on les factorise. Donc on colle ces bouts de code communs dans des .h et on les inclus. Je fais court.

                  Ensuite, avant d'arriver au cœur de ton problème observé. Ta classe "humain"  a un statu un peu bâtard -- classique des mauvais cours d'introduction à l'OO. D'où des remarques qui t'ont été faites. Techniquement parlant tu n'as en fait qu'un agrégat de données. Un agrégat de données, ça peut se copier, why not. Par contre on ne s'amuse pas en C++ à faire semblant que ce sont des classes au sens OO usuel (comme dans les mauvaises cours). Les agrégats n'ont aucun invariant, ils ne rendent aucun service. C'est juste des données foutues dans un même sac, agrégés ensemble. Le seul service de ces variables c'est de se souvenir pour nous de diverses informations. Elles ne feront rien. Donc puisque l'on ne s’embête pas à faire semblant, on met tout public et pas de getters et encore moins de setters -- même en Java cela a fini par ce savoir, les setters, c'est mal. Ailleurs on appelle ça des structures ou des enregistrements.

                  A l'inverse on a donc les classes qui vont rendre des services, et avoir des invariants (par exemple, un invariant pourrait être que le nom de l'humain ne puisse pas lui nuire, cf les "Mégane Renault" refoulés à l'administration qui enregistre les nouveaux nés (légende urbaine ou non, je n'en sais rien)). L'humain peut aussi faire des choses: manger, se déplacer, transporter des trucs, etc. Bref, nous sommes bien au delà de l'accumulation de données: nous entrons dans le paradigme objet.

                  Cas particulier (et parenthèse avancée), ces objets qui vivent leur vie, qui évoluent, et ne sont pas comparables aux autres (c'est quoi l'égalité entre deux humains? En cm? En grammes?), on va dire que ce sont des "entités". Et ces entités, ce n'est pas fait pour être manipulé par valeur avec un simple `humain h{paramètres];` -- il y a diverses incompatibilités techniques entre entités et duplication. On va les manipuler via des indirections, i.e. des pointeurs ou des références. Et contrairement à Java, on ne peut pas écrire `humain h = new humain....` (que le message d'erreur indique, malgré ton code qui diffère), le résultat de new doit aller dans un pointeur.

                  Et maintenant, depuis 9ans, on n'a plus aucune excuse valable pour manipuler directement des pointeurs (c'est source de plein de problèmes). C'est pour cela que mes VDD ont parlé de unique_ptr.

                  Comment résoudre ton problème? La vraie bonne réponse absolue dépend des objectifs pour ton humain (agrégat VS être autonome pouvant nous rendre des services). Et pratique cela va dépendre de ce qui est attendu de toi. Genre un exo demandé par un prof peut vous demander de faire des trucs qu'il ne faut surtout  pas faire dans des vrais codes.

                  Donc. As-tu des consignes? Si oui lesquelles? Si non, que cherches-tu à modéliser ou à travailler?

                  PS:

                  @totosayen, Si l'humain est un agrégat, il y a des changes non négligeables que l'on n'ait que faire que cela soit pré-initialisé à une valeur neutre. Si c'est une entité, il faut qu'une fois construit les invariants de l'objet soit positionnés, et le premier invariant est "est exploitable sans avoir à se poser de questions". Hors, si je peux avoir `humain{}`, j'ai encore plein de questions à me poser derrière: le nom, l'age, la taille, etc. Bref, quand un objet dispose d'un id (e.g. son nom), avoir un constructeur par défaut, je trouve ça extrêmement louche. Les constructeurs par défaut, c'est plus un truc de classes à sémantique de valeur.

                  EDIT: Si tu veux vraiment  apprendre le C++, suis plutôt le cours de ZdS (même la bêta sera mieux que ce que tu as vu jusqu'à présent). Et en exo: le javaquarium.

                  -
                  Edité par lmghs 12 avril 2021 à 22:59:58

                  • 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.
                    12 avril 2021 à 20:58:24

                    alpacinonogeek a écrit:

                    Alors premièrement, il faut que vous sachiez que me critiquer ou me rabaisser n'arrangera rien.

                    On ne te rabaisse pas, on t'indique tes erreurs, nuance.

                    Mais si tu n'acceptes de nous que te caresser dans le sens du poil et corriger les erreurs qui font que ton code ne compile pas, vas-y, fait toi plaisir et deviens un très mauvais programmeur (là, je te rabaisse).

                    Qu'on soit d'accord, programmer ne se résume pas à pisser des lignes de code qui compilent.
                    Ta démarche doit être clair, lisible, compréhensible, et avoir un sens.

                    Donc autant prendre les bonnes habitudes le plus tôt possible, faute de quoi, tu devras désapprendre tout ce que tu as appris.

                    PS: Pour répondre à ta première question, l'instanciation d'un objet se fait comme n'importe quelle variable, on declare le type, le nom, et si besoin, les valeurs d'initialisation entre accolades.

                    int myNumber{ 3 };    // declaration d'un entier
                    voiture maVoiture{ "Peugeot", "205" };    // declaration d'un objet de type voiture

                    -
                    Edité par Deedolith 12 avril 2021 à 21:07:08

                    • Partager sur Facebook
                    • Partager sur Twitter
                      12 avril 2021 à 21:24:47

                      -

                      -
                      Edité par Yann Amard 12 avril 2021 à 23:04:18

                      • Partager sur Facebook
                      • Partager sur Twitter
                        13 avril 2021 à 0:19:39

                        totosayen_cpp a écrit:

                        Et juste comme ça, autoriser déplacement, c'est cohérent dans ce contexte non ?

                        Pour une classe a sémantique d'entité, le déplacement est indiqué.
                        Dans le contexte ici présent, le constructeur de déplacement fournit par le compilateur est suffisant, inutile de le définir.
                        • Partager sur Facebook
                        • Partager sur Twitter
                          13 avril 2021 à 0:54:44

                          Deedolith a écrit:

                          totosayen_cpp a écrit:

                          Et juste comme ça, autoriser déplacement, c'est cohérent dans ce contexte non ?

                          Pour une classe a sémantique d'entité, le déplacement est indiqué.
                          Dans le contexte ici présent, le constructeur de déplacement fournit par le compilateur est suffisant, inutile de le définir.


                          J'avais raté la question.

                          J'ai une intuition contraire. L'affectation par déplacement n'est pas plus compatible avec les hiérarchies publiques que l'affectation par copie. Pour les deux affectations, on a des problèmes de slicing. Et donc une part non négligeable des entités n'est pas affectable par déplacement. Par généralisation j'en arrive que déplacement et entités ne font pas bon ménage. Après, il ne faut pas oublier entités VS valeurs, c'est une simplification. Il y a d'autres choses: les agrégations (maladroitement getterisées, bien souvent quand on croit que mieux == OO, et que l'on confond OO avec bases de données et donc que l'on croit que OO => getters),  les capsules RAII que le tuto du ZdS range dans les "handles", les exceptions, etc. (dans le papier un peu beaucoup à l'origine de nos pinaillages, il y avait deux autres catégories: https://web.archive.org/web/20201113224849/http://www.two-sdg.demon.co.uk/curbralan/papers/ObjectsOfValue.pdf )

                          Les handles RAII devront être déplaçables parce que c'est fort pratique d'avoir des fonctions sources/factories de capsules RAII. Est-ce des entités au sens usuels? Pas vraiment à mon goût.

                          Dans tous les cas, pour un type "humain" comme ici
                          - Si le type modélise une agrégation de données => règle de 0 pour moi. Si c'est copiable et/ou déplaçable tant mieux, sinon tant pis.
                          - Si le type modélise une vraie entité => Ni copiable, ni déplaçable en ce qui me concerne.

                          -
                          Edité par lmghs 13 avril 2021 à 1:03:20

                          • 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 avril 2021 à 9:21:22

                            Merci à vous deux pour vos précisions.

                            Si on part sur l'exemple d'une classe qui simule un humain, est ce que mettre des getters (mais pas de setters) serait pertinent, pour par exemple connaître l'âge d'une personne mais qu'on ne peut bien sûr pas modifier.

                            Et vu qu'en vrai un humain est autonome, comment ferait la classe humain pour grandir pendant l'exécution ?

                            Du multithreading dans le constructeur où un thread appelle successivement les fonctions nécessaires ?

                            (Je crois que je suis parti trop loin mais ça m'intéresse)

                            • Partager sur Facebook
                            • Partager sur Twitter
                              15 avril 2021 à 15:54:28

                              Salut,

                              De manière générale, tu ne dois pas penser à ta classe comme à la somme des données qu'elle contient, mais bien comme "des éléments" capables de rendre "un certain nombre" de services aux personnes qui les manipuleront (qui pourront être toi-même ou quelqu'un d'autre).

                              Il existe deux grandes "sortes" de services possibles que tes classes peuvent rendre:

                              • elles peuvent devoir répondre à certaines questions (quel est ton nom?, quel est ton age?, quelle est ta date de naissance?, combien mesure-tu? es tu un garçon? es tu une fille?, ...)
                              • ou elles peuvent devoir obéir à un ordre (réveille toi!, mange!, déménage( à telle adresse)!, déplace toi (vers tel endroit, ou de telle distance vers l'avant et de telle autre distance sur le coté), ...)

                              L'idée générale est d'abord de réfléchir au services que ta classe devra être en mesure de rendre, afin déterminer quelles données seront nécessaires pour lui permettre de le faire.

                              Au niveau des questions que tu voudras pouvoir poser à (une instance de) ta classe, il se peut (mais rien n'est encore moins sur) que la réponse qui sera donnée corresponde -- effectivement -- à l'une des données que tu as décidé de donner à ta classe (pour lui permettre de fournir les services que tu attends de sa part).

                              Par contre, il arrive souvent que la réponse doive être "évaluée" à partir de la valeur d'une de ces données: il n'y a, par exemple, aucun intérêt à fournir une donnée représentant l'âge d'une personne, pour la simple et bonne raison que cette valeur va changer une fois par an.

                              En revanche, l'age d'une personne peut très facilement être calculé à partir de sa date de naissance et de la date courante car, même si la date courante change tous les jours, la date de naissance ne sera jamais modifiée.  De plus, si tu donnes la date de naissance à ta classe, tu te donnes aussi la possibilité de décider qu'une question demandant la date de naissance peut représenter un service intéressant.

                              Au final, on se rend donc bien compte que les accesseurs ne seront réellement intéressant que ... s'ils correspondent effectivement à un service que l'on souhaite que la classe devra être en mesure de rendre.

                              Ensuite, il semble aussi évident que, si l'on décide déjà de ne pas fournir d'accesseur sur une donnée particulière, il n'y aura -- a priori -- aucune raison d'y adjoindre un mutateur.

                              De plus, même lorsqu'un accesseur s'avère intéressant, un mutateur n'aura que très rarement de l'intérêt, pour la simple raison que les services que l'on demande à notre classe de rendre ont tous un objectif primordial: éviter à l'utilisateur (dont il n'est pas faux de partir du principe que c'est un imbécile distrait) de faire une bêtise, d'oublier une règle ou une autre en le laissant calculer lui-même la nouvelle valeur à donner à une donnée quelconque.

                              totosayen_cpp a écrit:

                              Et vu qu'en vrai un humain est autonome, comment ferait la classe humain pour grandir pendant l'exécution ?

                              Heu... que veux tu dire par là?

                              Car, une fois que tu as définis "un certain nombre de services" qui doivent être rendus par ta classe, et que tu as compilé ton code, tu es forcément limité par les services que tu as prévu que ta classe serait en mesure de rendre.

                              Tu peux -- éventuellement -- décider de créer une nouvelle classe qui hérite de la première (et qui fournit donc les mêmes services "de base") pour laquelle tu décidera d'une "nouvelle série de services supplémentaires" que la nouvelle classe sera également en mesure de rendre.

                              Cependant, à l'exécution, une fois que tu auras créé une instance de ton humain "de base", il te sera impossible de décider -- à un moment quelconque -- de transformer ton instance d'humain en une instance de "cette autre classe" basée sur la classe humain.

                              • 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

                              Création d'un objet

                              × 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