Partage
  • Partager sur Facebook
  • Partager sur Twitter

Heritage et constructeur - question

Comment afficher une info avant l'appel du constructeur de classe mere

Sujet résolu
    15 octobre 2020 à 22:29:16

    Bonjour à tous,

    Ma question est: lorsque je crée un objet d'une classe fille, comment afficher quelque chose de la part du constructeur de la classe fille avant que le constructeur de la classe mère s'exécute ?

    Pour être plus claire, dans mon projet j'ai une classe mère Vehicule, et une classe fille Moto qui hérite de la classe Vehicule. La classe Vehicule comporte en attributs (protected) la vitesse et la masse. La classe Moto comporte en plus l'attribut prix. Ces attributs je les initialise depuis le constructeurs par défaut Moto() de façon interactive avec l'utilisateur, c'est à dire quand je crée un objet de type Moto comme cela : "Moto v;" alors automatiquement dans la console on me demande d'entrer les valeurs des attributs vitesse, masse, et prix. Pour cela, le constructeur par défaut de la classe mere Véhicule() permet la saisie interactive des attributs vitesse et masse, et le constructeur par défaut de la classe fille Moto() permet en plus la saisie interactive de l'attribut prix. En bref, la définition du constructeur de la classe fille Moto() est:

    Moto::Moto() : Vehicule()
    {
       int prix{};
       std::cout << "Le prix est : "; std::cin >> prix;
       m_prix = prix; // m_prix est le nom de l'attribut prix
    }

    Le fonctionnement normale lorsque je crée un objet de type Moto, dans l'ordre, c'est:
    1. Appel le constructeur de la classe mère Vehicule() --> initialise attributs vitesse et masse
    2. Appel le constructeur de la classe mère Moto() --> initialise attribut prix

    Mais là où je bloque, c'est qu'avant l'appel de la classe mère Vehicule() je souhaiterai afficher dans la console "MOTO" (le type de véhicule) avant que l'utilisateur saisie les attributs. Quelqu'un sait comme y parvenir ?

    -
    Edité par LucasV1 15 octobre 2020 à 22:35:18

    • Partager sur Facebook
    • Partager sur Twitter
      16 octobre 2020 à 0:33:22

      LucasV1 a écrit:

      Ma question est: lorsque je crée un objet d'une classe fille, comment afficher quelque chose de la part du constructeur de la classe fille avant que le constructeur de la classe mère s'exécute ?

      Pas possible.

      LucasV1 a écrit:

      de façon interactive avec l'utilisateur

      C'est une erreur de conception. Qu'est-ce qu'il se passe si l'utilisateur entre un prix invalide par exemple ?

      La logique devrait être :
      - on demande les infos a l'utilisateur
      - on vérifie que ces infos sont valides
      - si c'est valide, on construit l'objet directement valide
      - on utilise l'objet valide

      LucasV1 a écrit:

      Moto::Moto() : Vehicule()
      {
         int prix{};
         std::cout << "Le prix est : "; std::cin >> prix;
         m_prix = prix; // m_prix est le nom de l'attribut prix
      }

      Le fonctionnement normale lorsque je crée un objet de type Moto, dans l'ordre, c'est:
      1. Appel le constructeur de la classe mère Vehicule() --> initialise attributs vitesse et masse
      2. Appel le constructeur de la classe mère Moto() --> initialise attribut prix

      Mais là où je bloque, c'est qu'avant l'appel de la classe mère Vehicule() je souhaiterai afficher dans la console "MOTO" (le type de véhicule) avant que l'utilisateur saisie les attributs. Quelqu'un sait comme y parvenir ?

      C'est une mauvaise idée aussi de travailler avec une classe incomplète. Il faut attendre qu'elle soit finie, puis on l'utilise. Afficher les infos de la classe par exemple devrait probablement être fait en dehors du constructeur (sauf pour afficher des infos de debug par exemple).

      Donc ajoute un operateur << pour ostream (ou une fonction "print" si tu n'as pas encore appris la surcharge d'opérateurs) et mets dedans ce que tu veux afficher.

      • Partager sur Facebook
      • Partager sur Twitter
        16 octobre 2020 à 9:19:07

        gbdivers résume bien la situation.

        Il faut essayer de conceptualiser ton objet comme dans la vraie vie. Si tu désire acheter une moto, c'est le concessionnaire qui s'occupe d'encaisser, commander et te livrer ta moto.

        Ainsi le constructeur (et dans la majeure partie des cas) doit être le plus stupide possible.

        Si on voulait essayer de conceptualiser un catalogue de moto on devrait même peut-être utiliser un autre objet, du style MotoInfo car dans un catalogue la moto affichée est un autre élément que la moto physique réelle. La moto dans le catalogue est plutôt un ensemble (photo, prix, spécification techniques, catégorie).

        • Partager sur Facebook
        • Partager sur Twitter

        git is great because Linus did it, mercurial is better because he didn't.

          16 octobre 2020 à 11:39:03

          Pour aller encore plus loin, la programmation orientée objet porte plutôt mal son nom. Il faudrait plutôt parler de programmation orienté rôle (ou service), car ce que tu attends vraiment d'une classe est le rôle qu'elle va tenir dans le programme, le(s) service(s) qu'elle devra assurer. 

          Je ne crée jamais une classe pour le plaisir de créer une classe. Je la crée toujours avec la perspective du besoin fonctionnel, je crée une classe pour remplir une fonctionnalité très précise du programme, une classe par service à rendre, sinon c'est trop compliqué pour ma petite tête.

          Par exemple si je considère un traitement de fichier, je vais probablement envisager au moins 3 classes, une qui sera chargée de lire le fichier, une qui sera chargée de l'écrire, et d'autres pour exploiter son contenu.

          Lorsque je pense une classe, je pense toujours au service qu'elle doit assurer dans mon programme. Mes classes naissent de l'expression d'un besoin. J'ai besoin d'une fonctionnalité, je crée la ou les classes qui vont assurer cette fonctionnalité (rendre ce service), et je découpe les services compliqués en services plus simples que j'interconnecte (diviser pour régner), de sorte que j'ai presque toujours des classes conceptuellement simples (facile à écrire, code très court, facile à tester). Par exemple, pour écrire un fichier je vais probablement créer au moins 4 classes, la première transformera mes données mémoire en quelque chose que je peux écrire dans un fichier, la seconde sera chargée de la compression, la troisième du chiffrement et la dernière s'occupera d'aller écrire les données dans le fichier.

          Pour revenir aussi sur ce qu'a dit markand, il y a une règle d'or. Lorsque tu traites des données fournies par un utilisateur, la paranoïa aigüe est de rigueur, tu dois toujours considérer l'utilisateur comme au mieux un abruti et au pire un malveillant. Si tu permets à un utilisateur de faire une connerie, tu peux être absolument certain qu'il la fera tôt ou tard.

          • Partager sur Facebook
          • Partager sur Twitter
          Mettre à jour le MinGW Gcc sur Code::Blocks. Du code qui n'existe pas ne contient pas de bug
            16 octobre 2020 à 16:29:38

            Bonjour,

            Si ou oublie le fait que ta conception n'est pas la bonne, on peut tout à fait faire quelque chose avant la mère à condition que ça n'agisse ni sur la mère ni sur la fille qui n'existent pas encore au moment où est fabriquée la mère. Pour cela on utilise un double héritage avec un ancêtre "bidon" dont le seul intérêt est d'exister avant la mère:

            struct Display {
               Display( std::string_view str ) {
                  std::flush( std::cout << str );
               }
            };
            class Moto : private Display , public Vehicule { // attention de bien les mettre dans cet ordre
            public:
               Moto() : Display("C'est une Moto") {
                  std::flush( std::cout << "le prix est : " );
                  std::cin >> m_prix;
               }
            };
            La classe Display sera créée en premier (elle n'utilise pas même un octet en mémoire des objets Moto), elle affiche le texte, ensuite sont créés la partie Vehicule, puis tous les membres de Moto, pour finir le corps du constructeur est appelé pour récupérer le prix de la Moto.
            • Partager sur Facebook
            • Partager sur Twitter

            En recherche d'emploi.

              18 octobre 2020 à 14:33:28

              Je me suis mal exprimé. Je vais vous montrer en détail le programme que j'ai fais pour l'instant.

              Programme principal main():

                  // Programme qui demande a l'utilisateur quel type de vehicule il veut
                  int choix{-1};
                  Mobile *v;
                  float temps_ecoule{17.8};
              
                  while(choix != 0) {
              
                      std::cout << "Voulez-vous créer : 1 - Une Voiture / 2 - Un Deux roues / 0 - Quitter" << std::endl;
                      label_choix:
                      std::cin >> choix;
                      if (choix < 0 || choix > 2) {
                          std::cout << "ERREUR : choissiez 0, 1 ou 2" << std::endl;
                          goto label_choix;
                      }
              
                      switch (choix) {
                          case 0:
                              exit(0); // Le programme se finit
                              break;
                          case 1:
                              v = new Voiture;
                              break;
                          case 2:
                              v = new DeuxRoues;
                              break;
                          default:
                              std::cout << "ERREUR..." << std::endl;
                              exit(0);
                              break;
                      }
              
                      std::cout << "Position du mobile : " << v->calcul_position(temps_ecoule) << std::endl;
                      v->afficher();
              
                      delete v;
                  }

              Déclaration de la classe Mobile et de ses 2 classes filles (.h):

              /// Classe mere : Mobile
              
              class Mobile
              {
                  public:
                      Mobile();
                      Mobile(float vitesse, int immatriculation);
                      virtual ~Mobile();
                      float calcul_position(float temps); // temps en s
                      void setVitesse(float vitesse);
                      virtual void afficher() const = 0;
              
                  protected:
                      float m_vitesse; // en km_h
                      float m_position; // en km
              
                  private:
                      int m_immatriculation;
              };
              
              
              /// Classes filles : Voiture et DeuxRoues
              
              class Voiture : public Mobile
              {
                  public:
                      Voiture();
                      Voiture(float vitesse, int immatriculation, float masse, float consommation);
                      virtual ~Voiture();
                      virtual void afficher() const;
              
              
                  protected:
                      float m_masse; // kg
                      float m_consommation; // kg/km
              
                  private:
              
              };
              
              class DeuxRoues : public Mobile
              {
                  public:
                      DeuxRoues();
                      DeuxRoues(float vitesse, int immatriculation, float prix, int type);
                      virtual ~DeuxRoues();
                      virtual void afficher() const;
              
                  protected:
                      float m_prix;
                      int m_type; // 1 - Essence, 2 - Electrique
              
                  private:
                  
              };

              Définitions de la classe Mobile et de ses 2 classes filles (.cpp):

              /// Classe mere : Mobile
              
              Mobile::Mobile()
              {
                  float vitesse{}; int immatriculation{};
                  label_vitesse:
                  std::cout << "Vitesse du mobile (km/h) : "; std::cin >> vitesse;
                  if(vitesse < 0)
                  {
                      std::cout << "ERREUR : la vitesse doit etre positive" << std::endl;
                      goto label_vitesse;
                  }
                  std::cout << "Immatriculation du mobile : "; std::cin >> immatriculation;
                  m_vitesse = vitesse;
                  m_position = 0;
                  m_immatriculation = immatriculation;
              
                  //std::cout << "Constructeur Mobile par defaut" << std::endl;
              
              }
              
              Mobile::Mobile(float vitesse, int immatriculation) : m_vitesse(vitesse), m_position(0.0), m_immatriculation(immatriculation)
              {
                  std::cout << "Constructeur Mobile surcharge" << std::endl;
              }
              
              Mobile::~Mobile()
              {
                  std::cout << "Destructeur Mobile" << std::endl;
              }
              
              float Mobile::calcul_position(float temps)
              {
                  float position{}; // position en m, vitesse en km/h, temps en s
                  position = m_vitesse*temps;
                  position /= 3.6;
                  m_position = position;
                  return position;
              }
              
              void Mobile::setVitesse(float vitesse)
              {
                  m_vitesse = vitesse;
              }
              
              
              
              /// Classes filles : Voiture et DeuxRoues
              
              // Classe Voiture
              
              Voiture::Voiture() : Mobile()
              {
                  float masse{}, consommation{};
                  label_masse:
                  std::cout << "Masse de la voiture : "; std::cin >> masse;
                  if(masse <= 0)
                  {
                      std::cout << "ERREUR : la masse doit etre superieur à 0" << std::endl;
                      goto label_masse;
                  }
                  m_masse = masse;
                  label_consommation:
                  std::cout << "Consommation de la voiture : "; std::cin >> consommation;
                  if(consommation <= 0)
                  {
                      std::cout << "ERREUR : la masse doit etre superieur à 0" << std::endl;
                      goto label_consommation;
                  }
                  m_consommation = consommation;
                  std::cout << std::endl;
                  //std::cout << "Constructeur Voiture par defaut" << std::endl;
              }
              
              Voiture::Voiture(float vitesse, int immatriculation, float masse, float consommation) : Mobile(vitesse, immatriculation), m_masse(masse), m_consommation(consommation)
              {
                  //std::cout << "Constructeur Voiture surcharge" << std::endl;
              }
              
              Voiture::~Voiture()
              {
                  std::cout << "Destructeur Voiture" << std::endl;
              }
              
              void Voiture::afficher() const
              {
                  std::cout << "Voiture masse : " << m_masse << std::endl;
                  std::cout << "Voiture consommation : " << m_consommation << std::endl;
              }
              
              
              // Classe DeuxRoues
              
              DeuxRoues::DeuxRoues() : Mobile()
              {
                  float prix{};
                  int type{};
                  std::cout << "Prix de la moto : "; std::cin >> prix;
                  label_type:
                  std::cout << "Type de moto (1-Essence, 2-Electrique) : "; std::cin >> type;
                  if(type != 1 && type != 2)
                  {
                      std::cout << "ERREUR : le type est 1 ou 2" << std::endl;
                      goto label_type;
                  }
                  m_prix = prix;
                  m_type = type;
                  std::cout << std::endl;
                  //std::cout << "Constructeur DeuxRoues par defaut" << std::endl;
              }
              
              DeuxRoues::DeuxRoues(float vitesse, int immatriculation, float prix, int type) : Mobile(vitesse, immatriculation), m_prix(prix), m_type(type)
              {
                  //std::cout << "Constructeur DeuxRoues surcharge" << std::endl;
              }
              
              DeuxRoues::~DeuxRoues()
              {
                  std::cout << "Destructeur DeuxRoues" << std::endl;
              }
              
              void DeuxRoues::afficher() const
              {
                  std::cout << "Deux roues prix : " << m_prix << std::endl;
                  std::cout << "Deux roues type : " << m_type << std::endl;
              }

              N'étant pas encore très familier avec le C++, je préfère ne pas prendre de mauvaise habitude dès le départ.

              Donc dans le programme principal j'ai un pointeur v qui permet de prendre une des 2 classes filles (j'ai mis les destructeurs en virtual pour que ça fonctionne bien avec le 'delete v' à la fin). Donc dans les constructeurs c'est la que je définis que l'utilisateur doit entrer les caractéristiques (donc les attributs).

              Donc cette façon de faire est fausse même si le programme fonctionne ? Ce que je cherchais, c'était que dans le constructeur des classes filles je puisse afficher l'info de la classe fille qui va être construite ("Voiture" ou "Deux roues") avant que la classe mère soit appelé. Donc vous me dites que cela est impossible, mais il n'y a pas de moyens de contourner ce problème ?

              Je vous remercie


               

              markand a écrit:

              gbdivers résume bien la situation.

              Il faut essayer de conceptualiser ton objet comme dans la vraie vie. Si tu désire acheter une moto, c'est le concessionnaire qui s'occupe d'encaisser, commander et te livrer ta moto.

              Ainsi le constructeur (et dans la majeure partie des cas) doit être le plus stupide possible.

              Si on voulait essayer de conceptualiser un catalogue de moto on devrait même peut-être utiliser un autre objet, du style MotoInfo car dans un catalogue la moto affichée est un autre élément que la moto physique réelle. La moto dans le catalogue est plutôt un ensemble (photo, prix, spécification techniques, catégorie).



              • Partager sur Facebook
              • Partager sur Twitter
                18 octobre 2020 à 18:52:01

                Tu t'étais très bien exprimé, peut-être n'as-tu pas bien compris nos réponses.
                Et je t'ai fournis un moyen de mettre un affichage avant la construction du Mobile même si ta démarche est à éviter. On ne dois pas mettre d'interactions dans un constructeur et on ne dois jamais jamais utiliser de goto.

                • Partager sur Facebook
                • Partager sur Twitter

                En recherche d'emploi.

                  18 octobre 2020 à 20:31:22

                  Dalfab a écrit:

                  Tu t'étais très bien exprimé, peut-être n'as-tu pas bien compris nos réponses.
                  Et je t'ai fournis un moyen de mettre un affichage avant la construction du Mobile même si ta démarche est à éviter. On ne dois pas mettre d'interactions dans un constructeur et on ne dois jamais jamais utiliser de goto.


                  D'accord je vais enlever les interactions dans le constructeurs. C'est par convention ou habitude qu'on évite de faire ça ? Ou parce que ça risque de faire buguer le programme ?

                  Pourquoi ne jamais utiliser les goto ?

                  • Partager sur Facebook
                  • Partager sur Twitter
                    19 octobre 2020 à 7:28:43

                    LucasV1 a écrit:

                    D'accord je vais enlever les interactions dans le constructeurs. C'est par convention ou habitude qu'on évite de faire ça ? Ou parce que ça risque de faire buguer le programme ?

                    Si l'on n'a que ces deux solutions, nous dirons que c'est parce que cela risque de faire buguer le programme

                    Mais, pire encore, c'est surtout parce que les classes sont destinées à répondre à des besoins fonctionnels spécifiques.

                    C'est ainsi que nous séparons généralement l'ensemble du programme selon le modèle dit "MVC" (pour Modèle View Controler) dans lequel

                    • le modèle représente les données qui devront être manipulées
                    • la vue permet de présenter ces données "à l'extérieur" (que ce soit à l'utilisateur ou les envoyant vers un fichier ou un serveur distant)
                    • le contrôleur s'assure que les modifications apportées aux données depuis "certaines" vues soient cohérentes avec le modèle (qu'on n'essaye, par exemple, pas de définir un age à ... -5 ans, car cela n'aurait aucun sens :D )

                    Et, surtout, parce qu'il est beaucoup plus facile de gérer les projets importants lorsque l'on arrive à avoir une fonction pour chaque verbe de ton analyse fonctionnelle et un type de donnée ou une donnée pour chaque nom de celle-ci.

                    C'est ce que l'on appelle le "principe de la responsabilité unique": chaque chose ne s'occupe que d'un seul et unique problème, de manière à n'avoir finalement que peu de raison de modifier cette chose si son comportement ne change pas.

                    Par contre, cela permet, en combinant les différents éléments de manière différente, d'obtenir des comportements bien plus complexes, qui resteront basés sur des comportements particulièrement simples à comprendre et à manipuler ;)

                    Ainsi, lorsque tu souhaite faire afficher "ceci est une moto", tu travailles -- a priori -- au niveau  de la vue, vu que c'est un message que tu adresse à l'utilisateur de ton programme.

                    Ce n'est donc pas ... à la moto d'afficher ce message, vu qu'elle fait partie du modèle, mais bien à ce qui "manipule" la moto au niveau de la vue ;) (je simplifie énormément, mais le principe est bel et bien celui-là ;) )

                    LucasV1 a écrit:

                    Pourquoi ne jamais utiliser les goto ?

                    Ah, excellente question.

                    Le fait est qu'il y a toute une série de raisons pour éviter d'utiliser goto, parmi lesquelles on peut citer:

                    • le fait que goto tend très rapidement à transformer ton code en un gros plat de spagetti auquel il devient difficile de comprendre l'ensemble de la logique
                    • le fait qu'il y a toujours moyen de s'arranger pour éviter de l'utiliser
                    • le fait que C++ est un langage à exceptions et qu'il est difficile de gérer correctement ces exceptions lorsque l'on utilise goto (savais tu que std::cout risque de lancer une exception lorsque l'on essaye de provoquer l'affichage sur la sortie standard?)
                    • j'en passe, et sans doute de meilleures
                    • 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

                    Heritage et constructeur - question

                    × 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