Partage
  • Partager sur Facebook
  • Partager sur Twitter

justification des smarts pointers sur exemple

C++

    11 mars 2019 à 5:25:05

    Bonjour

    un argument que l'on entend souvent pour l'existence des smarts pointers, est de s'assurer que l'objet est effectivement détruit dès que l'on fait sur un pointeur brut (libération de la mémoire ici + destruction de l'objet). En faisant :

    delete p;

    on libère la mémoire.

    Si l'on est dans une portée simple, la libération de la mémoire libère, en principe, d'après mes connaissances, la mémoire contenant l'objet qui peut donc être détruit si il n'y a plus rien après le delete.

    Or j'ai fait un code très simple pour vor à quel moment précis intervient l'utilisation du destructeur. Je me serais attendu à ce que l'objet de la classe A soit détruit en toute fin après le dernier message d'affichage à l'écran. Et pourtant, il est détruit avant.

    Donc le problème de la destruction instantanée de l'objet pointée par le pointeur ptr1 ne se pose pas du tout en apparence.


    Pouvez-vous m'expliquer pour que je comprenne mieux pourquoi il est toujours mieux d' utiliser un pointeur intelligent, même dans ce cas ? 

    Il y a peut-être une subtilité que je n'ai pas saisie, merci de me l'expliquer.


    Auriez-vous un exemple de ce genre où l'on voit que si on utilise un pointeur brut , alors la destruction définitive de l'objet intervient trop tard ?

    Merci par avance

    #include <iostream>
    
    
    struct A {
        int id;
        A(){};
        A(int id) : id(id) { std::cout << "A " << id << '\n'; }
        ~A() { std::cout << "~A " << id << '\n'; }
    };
    
    int main(){
    
    A* ptr1 = new A(1);
    std::cout<<"Premier test"<<'\n';
    delete ptr1;
    
    std::cout<<"Ici je teste pour savoir à quel moment l'objet Foo construit est vraiment détruit"<<'\n';
    }
    
    
    
    



    -
    Edité par pseudo-simple 11 mars 2019 à 6:41:30

    • Partager sur Facebook
    • Partager sur Twitter
      11 mars 2019 à 6:19:19

      YES, man a écrit:

      Donc pourquoi utiliser un pointeur intelligent dans ce cas ?

      Désolé d'être celui qui doit t'annoncer cela, mais...

      - tu n'as pas compris la problématique des pointeurs nus

      - tu n'as pas compris l'intérêt des pointeurs intelligents

      Il faut que tu reprennes un cours C++ débutant au début.

      Bon lundi

      • Partager sur Facebook
      • Partager sur Twitter
        11 mars 2019 à 8:38:30

        Salut,

        Encore une fois, tu poses une question sans réfléchir.

        Si je te disais qu'un code aussi simple que

        int main(){
            int * ptr = new int[10];
            std::cout<<"salut\n";
            delete ptr;
        }

        est fondamentalement buggé, parce que std::cout provoque une allocation dynamique de la mémoire, et que, le simple fait qu'une allocation dynamique peut échouer (et lancer une exception) empêche de garantir que delete ptr sera appelé.

        Que devrais tu en déduire si tu arrivais à brancher ton cerveau (à condition que tu en aies un) ?

        • 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
          11 mars 2019 à 9:29:52

          Bonjour

          je suis d'accord pour l'argument de l'allocation dynamique qui peut échouer.

          Mais si on suppose que pour l'allocation dynamique c'est OK, pourquoi dans mon exemple avec la classe A, pourquoi le delete du pointeur sur un élément de type A entraîne-t-il immédiatement l'appel du destructeur de A alors que en principe, avec delete, la mémoire vient seulement d'être libérée ?

          L'objet n'est-il pas détruit en fin de scope après libération de la mémoire , donc après mon deuxième message ?

          Je vous remercie par avance d'éclaircir ce point précis.

          -
          Edité par pseudo-simple 11 mars 2019 à 9:40:23

          • Partager sur Facebook
          • Partager sur Twitter
            11 mars 2019 à 9:30:54

            Tu confonds encore et toujours pointeur et objet pointé. Cela t'a déjà été expliqué plusieurs fois dans tes sujets précédents.

            Ressortons les bons souvenirs (ils datent de même pas un mois je précise) :

            https://openclassrooms.com/forum/sujet/ifstream-et-ouverture-de-fichier

            https://openclassrooms.com/forum/sujet/unique-ptr-1

            Attention, le fort taux de modération peut heurter la sensibilité des plus jeunes.

            -
            Edité par Maluna34 11 mars 2019 à 10:05:30

            • Partager sur Facebook
            • Partager sur Twitter
              11 mars 2019 à 9:41:01

              C'est bien de s'interesser à la compréhension des mécanismes d'un langage de programmation, mais il faut aussi développer avec pour les assimiler et se construire un modèle mental intuitif de ce qui se passe ("machine notionnelle" https://computinged.wordpress.com/2016/03/07/notional-machines-and-misconceptions-in-cs-developing-a-research-agenda-at-dagstuhl/ =

              Sinon on empile les notions, sans les raccorder entre elles, on en oublie la moitié, on les comprend de traviole, on ne voit pas à quoi ça sert, et on mélange tout dans un horrible pâté.

              Dans ce cas : portée d'une variable, pointeur, objet pointé, destructeur,  appel explicite du destructeur,  libération d'une variable à la fin d'un bloc etc.

              Comme disait l'autre : que ceux qui étudient travaillent, que ceux qui travaillent étudient (confucius).

              -
              Edité par michelbillaud 11 mars 2019 à 9:45:00

              • Partager sur Facebook
              • Partager sur Twitter
                11 mars 2019 à 11:20:34

                YES, man a écrit:

                Mais si on suppose que pour l'allocation dynamique c'est OK, pourquoi dans mon exemple avec la classe A, pourquoi le delete du pointeur sur un élément de type A entraîne-t-il immédiatement l'appel du destructeur de A alors que en principe, avec delete, la mémoire vient seulement d'être libérée ?

                Parce que tu ne peux pas supposer que l'allocation dynamique c'est OK!!!

                La loi de l'emmerdement maximal joue contre toi : tout ce qui peut échouer échouera d'office.  Il ne sert donc à rien de te demander SI quelque chose qui peut échouer échouera... Tu gagnera énormément de temps en te demandant directement QUAND ce qui peut échouer échouera.

                Et la réponse est toujours la même : au pire moment qui soit!

                Quant au fait que le destructeur d'une classe (ou d'une structure) soit directement appelé quand une instance de la classe est détruite, ben, ca, c'est parce que les développeurs du langage ont été intelligents (pas comme toi, donc) et qu'ils se sont dits qu'il était cohérent de faire en sorte à ce que tout ce qui doit être entrepris pour détruire l'intégralité du contenu de la classe soit entrepris au moment où l'instance de la classe sera détruite, tout simplement ;)

                -
                Edité par koala01 11 mars 2019 à 20:52:39

                • 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
                  11 mars 2019 à 14:20:32

                  on détruit les classes en C++, maintenant ? :-)

                  • Partager sur Facebook
                  • Partager sur Twitter
                    11 mars 2019 à 20:53:18

                    j'ai corrigé mon intervention pour lui faire dire quelque chose de sensé en parlant d'instance de la classe ;)

                    Merci de la remarque :D

                    • 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
                      16 mars 2019 à 16:22:02

                      YES, man a écrit:

                      Auriez-vous un exemple de ce genre où l'on voit que si on utilise un pointeur brut , alors la destruction définitive de l'objet intervient trop tard ?

                      Merci par avance

                      #include <iostream>
                      
                      
                      struct A {
                          int id;
                          A(){};
                          A(int id) : id(id) { std::cout << "A " << id << '\n'; }
                          ~A() { std::cout << "~A " << id << '\n'; }
                      };
                      
                      int main(){
                      
                      A* ptr1 = new A(1);
                      std::cout<<"Premier test"<<'\n';
                      //delete ptr1; //--> Fuite de mémoire <--
                      
                      std::cout<<"Ici je teste pour savoir à quel moment l'objet Foo construit est vraiment détruit"<<'\n';
                      }
                      
                      Pas trop tard, le problème serait de ne pas appeler le delete. Pour conséquence, une fuite de mémoire, ou pire encore, appeler le delete trop tôt, ce qui engendrera probablement un Seg fault lors de son utilisation.

                      La règle d'or: Pour chaque appel de new, ça prend un appel de delete.

                      En gros, c'est risqué, l'utilisation de smart_ptr, gère tout ça pour toi.

                      YES, man a écrit:

                      Or j'ai fait un code très simple pour voir à quel moment précis intervient l'utilisation du destructeur. Je me serais attendu à ce que l'objet de la classe A soit détruit en toute fin après le dernier message d'affichage à l'écran. Et pourtant, il est détruit avant.

                      Tu confond peut-être:

                      Allocation dynamique (heap):
                      -malloc / free : C      -> allocation / libération, de zones mémoire,
                      -
                      new / delete: C++ -> allocation + appel constructeur /  appel destructeur + libération.

                      Allocation sur la pile (stack):
                      -Mémoire réservé d'avance, puis pile et dé-pile
                      -
                      Destruction automatique à la fin du scope (appel du destructeur):

                      #include <iostream>
                      
                      
                      struct A {
                          int id;
                          A(){};
                          A(int id) : id(id) { std::cout << "A " << id << '\n'; }
                          ~A() { std::cout << "~A " << id << '\n'; }
                      };
                      
                      int main(){
                      
                      A obj(1); //appel du constructeur
                      std::cout<<"Ici je teste pour savoir à quel moment l'objet Foo construit est vraiment détruit"<<'\n';
                      } //appel du destructeur
                      • Partager sur Facebook
                      • Partager sur Twitter

                      GZE, un moteur multiplateforme, adapté pour de la 2D, 3D et création de logiciels.

                        16 mars 2019 à 22:15:49

                        Merci Maeiky. Tu es le bienvenu pour intervenir si tu veux aider sur les prochains fils 

                        encore merci

                        -
                        Edité par pseudo-simple 16 mars 2019 à 22:16:38

                        • Partager sur Facebook
                        • Partager sur Twitter
                          17 mars 2019 à 7:59:11

                          Un peu de bon sens, on sait que new appelle le constructeur. Partant de là, il semble censé de penser que les architectes du langage ont été cohérents dans leurs choix de conception, et donc que delete appelle le destructeur...

                          Du reste, comment pourrait on appeler le destructeur sur une instance d'objet dont la mémoire a déjà été libérée? N'oublions pas que le tas recycle la mémoire, donc si delete n'appelle pas le destructeur, le destructeur sera appelé sur la mémoire potentiellement affectée à une autre variable qui n'a rien de commun avec celle qui est détruite, c'est juste absurde!

                          L'usage de pointeurs nus pour l'allocation dynamique, pose 3 problèmes:

                          • La fuite de mémoire, un new et pas de delete correspondant sur le chemin d'exécution.
                          • La libération multiple, un new et plusieurs delete associés sur le même chemin d'exécution.
                          • La réalité du terrain, on n'est pas dans le monde magique des bisounours, n'importe quelle erreur d'exécution peut survenir à n'importe quel moment et Murphy veille au grain, sa loi (dite de l'emmerdement maximum) s'applique toujours avec une "efficacité" redoutable.

                          L'emploi de pointeurs intelligents permet de se protéger de manière complète contre les deux premiers problèmes, et presque systématiquement contre le troisième.

                          • 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

                          justification des smarts pointers sur exemple

                          × 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