Partage
  • Partager sur Facebook
  • Partager sur Twitter

Class garage cours C++

    22 octobre 2021 à 19:57:50

    Bonjour à tous,

    Voilà je suis en train de faire la classe garage du fameux cours C++.

    J'essaye de supprimer un véhicule de mon garage. j'ai donc créé une fonction qui delete le vehicule selon son positionnement, puis j'initialise ce pointeur à 0. seulement voila une fois le pointeur mis à 0 impossible d'executer garage.liste(), ci dessous mon code :

    main.cpp:

        int main()
    {
    Garage liste_vehicules; // on crée un garage
    Vehicule v(10);
    Voiture vo;
    Moto m;
    liste_vehicules.ajouter(v);
    liste_vehicules.ajouter(vo);
    liste_vehicules.ajouter(m);// on ajout un vehicule
    liste_vehicules.lister();
    liste_vehicules.supprimer();
    
    liste_vehicules[0]->afficher();
    
        return 0;
    }
    h
    #ifndef VEHICULE_H
    #define VEHICULE_H
    #include <vector>
    #include <string>
    
    
    class Vehicule
    {
        public:
            Vehicule(int prix);
            virtual void afficher() const;
            virtual ~Vehicule();
        protected:
            int m_prix;
    };
    class Voiture : public Vehicule
    {
    public:
        Voiture();
        virtual void afficher() const;
        virtual ~Voiture();
    private:
        int m_nb_porte;
    };
    class Moto : public Vehicule
    {
        public:
        Moto();
        virtual void afficher() const;
        virtual ~Moto();
        private:
        int m_vitesse;
    };
    class Camion: public Vehicule
    {
    public :
        Camion(int prix,int nb_remorque);
        virtual~Camion();
        virtual void afficher() const;
    private:
        int m_nb_remorques;
    };
    class Garage
    {
    public :
        Garage();
        virtual ~Garage();
        void ajouter(Vehicule &vehic);
        void supprimer();
        void lister() const;
        Vehicule* operator[](int const& i);
    private :
        std::vector<Vehicule*> m_liste_vehicules;
    };
    void presenter (Vehicule const *vehic);
    
    #endif // VEHICULE_H
    #include "Vehicule.h"
    #include <iostream>
    #include <vector>
    
    Vehicule::Vehicule(int prix = 10000): m_prix(prix)
    {}
    Vehicule::~Vehicule()
    {}
    
    void Vehicule::afficher() const
    {
        std::cout << "le prix du vehicule est de " << m_prix << std::endl ;
    
    }
    
    Voiture::Voiture(): m_nb_porte(5)
    {
    
    }
    Voiture::~Voiture() {}
    
    void Voiture::afficher() const
    {
        std::cout << "le prix de la voiture est de " << m_prix <<" et elle a " << m_nb_porte << " porte" <<  std::endl ;
    
    }
    Moto::Moto(): m_vitesse(6)
    {
    
    }
    Moto::~Moto()
    {}
    void Moto::afficher() const
    {
        std::cout << "le prix de la moto est de " << m_prix << " et elle a " << m_vitesse << " vitesse" << std::endl ;
    
    }
    void presenter (Vehicule const *vehic)
    {
        vehic->afficher();
    }
    Camion::Camion(int prix, int nb_remorques = 0) : Vehicule(prix), m_nb_remorques(nb_remorques)
    {}
    Camion::~Camion()
    {}
    void Camion::afficher() const
    {
        std::cout << "le prix de ce camion est de " << m_prix << " et il peut tracter " << m_nb_remorques << " remorques" << std::endl;
    
    }
    Garage::Garage()
    {}
    Garage::~Garage()
    {
        int i(0);
        for(i; i<m_liste_vehicules.size(); ++i)
        {
            delete m_liste_vehicules[i];  //On libère la i-ème case mémoire allouée
            m_liste_vehicules[i] = 0;  //On met le pointeur à 0 pour éviter les soucis
        }
    
    }
    void Garage::ajouter(Vehicule &vehic)
    {
        Vehicule *ptr(0);
        ptr = &vehic;
        m_liste_vehicules.push_back(ptr);
    
    }
    void Garage::supprimer()
    {
       delete m_liste_vehicules[1];
       m_liste_vehicules[1] = 0;
    }
    void Garage::lister() const
    {
        int j(0);
        for (j; j< m_liste_vehicules.size(); j++)
    {
       presenter(m_liste_vehicules[j]);
    }
    }
    Vehicule* Garage::operator[](int const& i)
    {
       return m_liste_vehicules[i];
    }
    quelqu'un peut-il m'expliquer pourquoi s'il vous plait ?



    • Partager sur Facebook
    • Partager sur Twitter
      23 octobre 2021 à 6:42:26

      Il faudrait montrer un exemple

      • Ce qu'on met comme donnée
      • Ce que ça devrait faire
      • Ce que ça fait à la place (texte complet  des messages d'erreur etc)
      • Partager sur Facebook
      • Partager sur Twitter
        23 octobre 2021 à 10:48:58

        Bonjour,

        Ok je vais essayer de faire un exemple plus simple pour que ce soit plus facile à expliquer 

        par exemple lorsque j'exécute le programme ci dessous :

        1)

        #include <iostream>
        #include <vector>
        #include <string>
        
        
        int main()
        {
            int *ptr(0);
            //std::cout << " le ptr pointe sur cette donné : " << *ptr << std::endl; 
            // pour moi ceci devrait afficher rien : "" et fait bugger le programme
            std::cout << " ceci est l'adresse contenu dans ptr " << ptr << std::endl; // affiche 0
            ptr = new int;
            std::cout << " le ptr pointe sur cette donné : " << *ptr << std::endl; // " pour moi ceci devrait afficher un int peu importe lequel"
            std::cout << " ceci est l'adresse contenu dans ptr " << ptr << std::endl;// " affiche une adresse au hasard"
            *ptr = 5;
            std::cout << " le ptr pointe sur cette donné : " << *ptr << std::endl; // " pour moi ceci devrait afficher un int peu importe lequel"
            std::cout << " ceci est l'adresse contenu dans ptr " << ptr << std::endl;// " affiche une adresse au hasard"
            delete ptr;
            std::cout << " le ptr pointe sur cette donné : " << *ptr << std::endl;
            /* pour moi ceci devrait afficher rien "" car je viens de suprimmer l'objet contenu dans le pointeur
             or elle pointe sur une nouvelle donnée*/
            std::cout << " ceci est l'adresse contenu dans ptr " << ptr << std::endl;
            // l'adresse du pointeur reste la même, ça je comprends bien
            ptr = 0;
            std::cout << " le ptr pointe sur cette donné : " << *ptr << std::endl;
            // cette ligne fait bugger le programme je ne comprends pas pourquoi
            std::cout << " ceci est l'adresse contenu dans ptr " << ptr;
        
            return 0;
        }
        

        j'ai mis les commentaires de ce que cela devrait faire.

        2) ce que donne l'exécution du programme :

        Question :

        1) Alors mon programme se compile bien, cependant lorsque je demande d'afficher le contenu du ptr(0) le reste du programme ne s'affiche pas:

        je ne suis pas forcement choqué mais j'aimerai bien comprendre ce qui se passe la dedans pour moi dans la "case mémoire" 0 il n'y a rien, donc le programme ne devrait rien afficher mais je ne comprends pas pourquoi il empêche la suite du programme de s'exécuter.

        2) -lorsque j'assigne un new ptr, le ptr obtient une nouvelle valeur (il contient une adresse au hasard donnée par l'ordinateur) ainsi qu'un valeur int au hasard. c'est bien ce qui se passe.

         -lorsque j'assigne à *ptr la valeur 5 je m'attends à ce que ptr conserve sont adresse et que *ptr contienne la valeur 5  

        - La ça se complique, lorsque de delete le ptr, je m'attends à ce que ptr garde sont adresse (ça c'est bien ce qui se passe) par contre je m'attendais à ce que ptr pointe sur rien. Cependant ce n'est pas le cas ptr pointe à nouveau sur une valeur int au hasard.

        Du coup je ne voit pas l'intérêt de libéré un pointeur vu qu'il point sur un nouvel objet... en faite je ne libère rien du tout.

        j'espère que je suis assez claire ... Merci d'avance :) 

        -
        Edité par MercierMercier 23 octobre 2021 à 10:52:40

        • Partager sur Facebook
        • Partager sur Twitter
          23 octobre 2021 à 10:55:21

          MercierMercier a écrit:

          je demande d'afficher le contenu du ptr(0)

          C'est interdit de faire cela. C'est un comportement indéterminé et c'est normal que ton programme fasse n'importe quoi ensuite.

          MercierMercier a écrit:

          lorsque de delete le ptr, (...) je m'attendais à ce que ptr pointe sur rien.

          C'est interdit de faire cela. C'est un comportement indéterminé et c'est normal que ton programme fasse n'importe quoi ensuite.

          MercierMercier a écrit:

          Du coup je ne voit pas l'intérêt de libéré un pointeur vu qu'il point sur un nouvel objet... en faite je ne libère rien du tout.

          j'espère que je suis assez claire ... Merci d'avance :) 

          Si, la mémoire est libérée.

          ---------------------------------------------------------------------------------------------------

          Hors sujet : il y a pleins de choses qui ne vont pas dans ton code 

          - utiliser 0 pour initialiser un pointeur

          - utiliser les parenthèses pour initialiser une variable

          - utiliser des pointeurs nus

          - indentation de ton code

          - utilise override

          - const& dans ajouter()

          - pas de const& dans []

          - initialise tes variables dans les déclarations de classe

          - le delete dans le destructeur de Garage et dans supprimer() sont invalides

          - ne définie pas `int i` en dehors du for

          - pourquoi presenter() est une fonction libre ?

          Bref, change de cours.

          -
          Edité par gbdivers 23 octobre 2021 à 11:03:26

          • Partager sur Facebook
          • Partager sur Twitter
            23 octobre 2021 à 14:55:50

            Bonjour,

            je vais essayer de préciser les réponses de gbdivers. La notion de pointeur et de pointé est une confusion fréquente quand on débute.

            MercierMercier a écrit:

            je demande d'afficher le contenu du ptr(0)

            Un pointeur c'est fait pour pointer. Quand il est nul, la zone pointée ne correspond à rien de valide. On ne doit plus essayer d'utiliser la zone pointée à partir de cet instant.

            MercierMercier a écrit:

            lorsque de delete le ptr, (...) je m'attendais à ce que ptr pointe sur rien.

            Attention le code delete ptr correspond à la phase "détruire l'objet indiqué par le pointeur et libérer la mémoire dynamique qui a été réservée à cet endroit". Donc après avoir appelé delete ptr, la zone est désormais rendue au système mais le pointeur lui est inchangé, il ne pointe pas sur rien, il pointe sur une zone qu'il ne faut surtout pas utiliser. C'est pourquoi on le met souvent à nul après cela pour mieux voir qu'il ne faut pas l'utiliser.

            MercierMercier a écrit:

            Du coup je ne voit pas l'intérêt de libéré un pointeur vu qu'il point sur un nouvel objet... en faite je ne libère rien du tout.

            j'espère que je suis assez claire ... Merci d'avance :) 

            Ne pas confondre le pointeur (qui ne contient qu'un simple nombre) avec sa zone pointée. Il faut évidement impérativement la libérer. Mais attention dans ton code tu n'a jamais alloué de mémoire donc rien n'est à libérer ici.

            Les new et delete c'est compliqué. Il y cependant trois règles simples pour savoir comment s'en servir.
            1) On ne doit jamais utiliser new et delete
            2) Mais si on a besoin de faire des allocations dynamiques. Il existe des objets de la bibliothèque standard qui gèrent bien cela. Et on n'oublie pas la première règle.
            3) Les années passant, on devient expert et alors on ne doit surtout pas oublier la première règle.

            En précision je reviens sur les erreurs de la fonction Garage::supprimer()
            - Tu décides de supprimer le second véhicule. Que va-t-il se passer s'il y a moins de véhicules dans le garage?
            - Tu utilises delete (voir règles 1 et 2), et en plus il n'y a jamais eu de new! C'est parce qu'on se trompe toujours qu'il a cette règle.
            - Tu remplaces le pointeur par 0, il faut utiliser la valeur nullptr.
            - et donc tu te retrouves avec un pointeur à ne surtout pas utiliser en plein milieu de ton garage.
            La solution : ne surtout pas mettre quelque chose à la place du pointeur mais vraiment ôter la case du tableau.

            void  Garage::supprimer_le_second()
            {
               if ( l_liste_vehicule.size() > 1u ) // au moins 2 véhicules
                  m_liste_vehicules.erase( m_liste_vehicules.begin()+1 ); // ôter le 2nd
            }

            -
            Edité par Dalfab 23 octobre 2021 à 14:59:24

            • Partager sur Facebook
            • Partager sur Twitter

            En recherche d'emploi.

              25 octobre 2021 à 16:53:09

              Bonjour,

              Merci pour vos réponse !

              Alors je reprends un peu les questions mais peut-être dans un ordre différent :

              a) Bref, change de cours.. Alors pour le coups c'est pas une question mais l'utilisation des pointeurs dynamiques est montré dans le cours.

              c'est vrai que j'ai vu d'autre tuto sur les pointeurs uniques, pointeurs partagés, mais personnellement je me suis dis qui peut le plus peut le moins et donc j'ai voulu continuer avec les ptr normaux. Je pense que en effet je vais changer de cours mais j'aimerai bien comprendre tout de même pourquoi ces pratiques ne sont pas bonnes et le fonctionnement des pointeurs.

              b)utiliser 0 pour initialiser un pointeur. 

              Il est dit de faire comme ça dans le tuto pour assigner 0 à un pointeur (c'est pour cela que je l'ai fait dans ma fonction ajouter), donc si j'ai bien compris il faut modifier par int *ptr; ptr= nullptr;  ?

               utiliser les parenthèses pour initialiser une variable

              ok pourquoi ? 

              - utiliser des pointeurs nus

              Ok donc utiliser un "pointeur nu" c'est mal ou alors c'est pas conseillé pour les débutants ?  

              - indentation de ton code

              ok j'imagine qu'il y a des normes je vais chercher 

              - utilise override

              ok je viens de voir ce que c'était, je vais essayer 

              - const& dans ajouter()

              ok je suis d'accord avec le principe vu que l'on ne modifie pas l'objet on prend seulement son adresse pour l'assigner à une variable de type pointeur. mais quand je le modifie j'ai cela "error: invalid conversion from 'const Vehicule*' to 'Vehicule*' [-fpermissive]|"

              - pas de const& dans []

              La je comprends pas pourquoi parce que pour moi la fonction "[]" le modifie pas la variable en argument 

              - initialise tes variables dans les déclarations de classe

              Tu veux dire lors de la déclaration dans le main ? genre : "Voiture vo(10000,5);" ?

              - le delete dans le destructeur de Garage et dans supprimer() sont invalides

              Si j'ai bien compris ça recoupe avec la remarque de Dalfab qui dit qu'il n'y a pas de "delete" sans "new" ?

              Alors j'en profite juste pour bien comprendre dans le code de ajouter(), voila ce qu'il veut dire pour moi (en commentaire).

              void Garage::ajouter(Vehicule &vehic)// détaille d'une fonction "ajouter" qui passe par référence un argument de type vehicule
              {
                  Vehicule *ptr(0);// je crée un pointeur nommé "ptr", qui pointe sur 0. 
                  ptr = &vehic; // je stocke dans mon pointeur l'adresse du vehicule entré en argument
                  m_liste_vehicules.push_back(ptr); // je stocke au fond de mon vecteur de pointeurs de vehicules mon nouveau pointeur 
              
              }

              Comme je n'ai pas utiliser "new" mon pointeur ptr est-il un pointeur nu ?

              Dalfab dit que rien n'est a libérer, ça veux dire que je peux utiliser mon pointeur sans le libéré c.a.d. j'utilise un pointeur sans allocation dynamique, ce qui n'est pas grave ?

              je viens de me rendre compte que j'ai mis dans ptr l'adresse d'une variable sans utilisé new, je ne comprends donc pas à quoi sert la fonction new du coup ?

              bref c'est juste des questions pour comprendre je vais essayer de modifier avec des pointeurs intelligents.

              - ne définie pas `int i` en dehors du for

              ok pourquoi ? 

              - pourquoi presenter() est une fonction libre ?

              Eh bien parce que c'est comme cela que c'est expliqué dans le cours et que je n'y vois pas d'inconvénient... Je pourrais utiliser la fonction afficher() mais c'est vrai que je trouve la syntaxe présenter(variable) plus facile à utiliser dans le main.

              Si la question est pourquoi ne pas la rattacher à la classe véhicule c'est parce que j'imagine que sinon il utilisera par défaut la fonction afficher, je me trompe ?

              Merci encore pour les explications

              • Partager sur Facebook
              • Partager sur Twitter
                25 octobre 2021 à 17:08:48

                Bon, toujours la confusion entre pointeur et objet pointé.

                1. Quand on évalue l'expression  new Garage();  

                • le "runtime C++" (la mécanique qui est derrière) demande à l'allocateur mémoire de réserver et de lui fournir un espace assez grand pour y loger sizeof(Garage) octets
                • il exécute ensuite le code du constructeur de Garage pour - comme son nom ne l'indique pas exactement - initialiser correctement cet espace mémoire
                • le résultat de l'expression est l'adresse de cet espace, de type  Garage*
                2. Quand on fait  Garage *g = new Garage("Peugeot"); le résultat (l'adresse) est stocké dans le pointeur g
                3. Si ensuite on exécute    delete g;
                • le runtime exécute le code du destructeur de g
                • l'allocateur est prévenu que l'espace occupé par l'objet peut être récupéré
                Important :
                • le "delete g"  ne modifie pas le contenu du pointeur g, qui contient toujours la même adresse
                • mais maintenant, le programmeur doit considérer cette adresse comme invalide, parce que le runtime a pu faire autre chose de cet espace mémoire
                • par conséquent, le programmeur ne doit pas déréférencer le pointeur (c-a-d utiliser *g ou g->quelquechose() ou delete g) qui contient une adresse maintenant invalide.
                • par contre il peut y mettre autre chose, par exemple g = new Garage("Simca");  
                • ou encore y mettre nullptr, qui est une valeur spéciale qui sert à dire "l'adresse de rien".
                Donc en résumé un pointeur peut contenir
                • n'importe quoi parce qu'il n'a pas encore été initialisé 
                • l'adresse valide d'un objet alloué et pas libéré (ok)
                • l'adresse invalide d'un objet qui a été libéré
                • la valeur nullptr
                Il n'y a que dans le second cas qu'on peut légitimement déréférencer le pointeur.

                -
                Edité par michelbillaud 25 octobre 2021 à 17:28:13

                • Partager sur Facebook
                • Partager sur Twitter
                  25 octobre 2021 à 23:40:20

                  MercierMercier a écrit:

                  je me suis dis qui peut le plus peut le moins

                  Qui te dit que les pointeurs nus sont "le plus" et les pointeurs intelligents "le moins" ?

                  Ceux qui apprennent les pointeurs nus se perdent souvent dans des détails syntaxiques et des bugs de pointeurs invalides. Ceux qui utilisent les pointeurs intelligents peuvent se focaliser plus sur l'apprentissage de notions plus intéressantes, comme l'ownership, le lifetime, le RAII, la gestion correcte de la mémoire, la conception logiciel, les bonnes pratiques, etc.

                  MercierMercier a écrit:

                  mais j'aimerai bien comprendre tout de même pourquoi ces pratiques ne sont pas bonnes et le fonctionnement des pointeurs.

                  Le fait que l'on a des bugs avec les pointeurs nus, même les experts en C et en C++, ce n'est pas une raison suffisante pour ne pas les utiliser ?

                  MercierMercier a écrit:

                  il faut modifier par int *ptr; ptr= nullptr;  ?

                  Non. Ecris "int* ptr = nullptr".

                  MercierMercier a écrit:

                  utiliser les parenthèses pour initialiser une variable

                  ok pourquoi ? 

                  Quel est l'argument technique pour utiliser les parenthèses plutôt que les autres façons d'initialiser ?

                  Si tu ne connais pas les arguments techniques pour choisir les parenthèses, pourquoi te faut-il des arguments pour corriger les erreurs que tu as appris mais que tu n'as pas demandé "pourquoi" pour les parenthèses ?

                  MercierMercier a écrit:

                  Ok donc utiliser un "pointeur nu" c'est mal ou alors c'est pas conseillé pour les débutants ?  

                  Dans l'absolue, rien n'est "mal". La majorité des syntaxes a ses raisons d'être. Mais les pointeurs nus sont piégeux et souvent utilisé sans bonne raison. Ce qui fait au final que les pointeurs nus sont "mal" pour les débutants.

                  MercierMercier a écrit:

                  - const& dans ajouter()

                  ok je suis d'accord avec le principe vu que l'on ne modifie pas l'objet on prend seulement son adresse pour l'assigner à une variable de type pointeur. mais quand je le modifie j'ai cela "error: invalid conversion from 'const Vehicule*' to 'Vehicule*' [-fpermissive]|"

                  C'est ce que tu devrais avoir pour respecter la const-correctness. Mais je ne suis pas surpris que cela ne compile pas.

                  MercierMercier a écrit:

                  - pas de const& dans []

                  La je comprends pas pourquoi parce que pour moi la fonction "[]" le modifie pas la variable en argument 

                  Parce qu'il ne faut pas passer les types fondamentaux par référence constante.

                  MercierMercier a écrit:

                  - initialise tes variables dans les déclarations de classe

                  Tu veux dire lors de la déclaration dans le main ? genre : "Voiture vo(10000,5);" ?

                  Non. Ecris :

                  class Voiture : public Vehicule
                  {
                      Voiture() = default;
                  ...
                      int m_nb_porte = 5;
                  };
                  

                  MercierMercier a écrit:

                  Comme je n'ai pas utiliser "new" mon pointeur ptr est-il un pointeur nu ?

                  Si, c'est un pointeur nu.

                  MercierMercier a écrit:

                  Comme je n'ai pas utiliser "new" mon pointeur ptr est-il un pointeur nu ?

                  Dalfab dit que rien n'est a libérer, ça veux dire que je peux utiliser mon pointeur sans le libéré c.a.d. j'utilise un pointeur sans allocation dynamique, ce qui n'est pas grave ?

                  je viens de me rendre compte que j'ai mis dans ptr l'adresse d'une variable sans utilisé new, je ne comprends donc pas à quoi sert la fonction new du coup ?

                  bref c'est juste des questions pour comprendre je vais essayer de modifier avec des pointeurs intelligents.

                  Cf la réponse de michelbillaud.

                  MercierMercier a écrit:

                  - pourquoi presenter() est une fonction libre ?

                  Eh bien parce que c'est comme cela que c'est expliqué dans le cours et que je n'y vois pas d'inconvénient... Je pourrais utiliser la fonction afficher() mais c'est vrai que je trouve la syntaxe présenter(variable) plus facile à utiliser dans le main.

                  Si la question est pourquoi ne pas la rattacher à la classe véhicule c'est parce que j'imagine que sinon il utilisera par défaut la fonction afficher, je me trompe ?

                  Ca n'a juste pas de sens de faire une telle fonction.

                  • Partager sur Facebook
                  • Partager sur Twitter

                  Class garage cours C++

                  × 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