Partage
  • Partager sur Facebook
  • Partager sur Twitter

Fonction avec new et delete (suite)

Pourquoi donc ?

Sujet résolu
    11 octobre 2021 à 22:06:04

    Bonsoir,

    Dans le post précédent, Koala01 a dit:

    >  5- on ne met jamais new et delete dans une même fonction

    Pourquoi donc ?

    Si j'avais une variable locale, trop grosse pour être mise dans la pile, j'utiliserai une procédure avec un new (ou alors un constructeur de l'objet qui alloue la variable) au début, je ferais le calcule sur cette variable, et je la détruirai ensuite (ou alors j'appellerai le destructeur de l'objet, qui déléterait cet objet). Ce n'est pas comme ça qu'il est souhaitable de faire ?

    Bien cordialement

    • Partager sur Facebook
    • Partager sur Twitter
      11 octobre 2021 à 22:31:59

      Je suppose qu'il dit cela a propos des exceptions. Si tu as :

      void foo() {
          auto p = new int;
          ... bla bla bla
          delete p;
      }

      Si une exception est lancée, tu auras une fuite mémoire.

      C'est possible de catch les exceptions :

      void foo() {
          auto p = new int;
          try {
              ... bla bla bla
          }
          catch(...) {
              delete p;
          }
          delete p;
      }

      Mais cela devient très très vite impossible à gérer si tu as plusieurs pointeurs (puisqu'il faut un try-catch pour chaque new, puisque new peut lancer une exception).

      La seule solution clean exception safe, c'est de faire comme il dit : utiliser les constructeurs/destructeurs ou une classe RAII existante.

      • Partager sur Facebook
      • Partager sur Twitter
        11 octobre 2021 à 23:43:33

        Mon interprétation de son point est qu'il faille dans l'ordre

        1- préférer mettre la variable sur la pile

        2- et (deux) si ce n'est vraiment pas possible parce qu'elle est trop grosse (je n'en connais pas de la sorte qui ne soient pas des tableaux qui ne peuvent être résolus avec des vecteurs), `auto p = make_unique....`

        -
        Edité par lmghs 12 octobre 2021 à 14:16:48

        • 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 octobre 2021 à 1:56:34

          Salut,

          Ce qui est bien, c'est que l'on a déjà répondu pour moi :D

          Je n'ai donc rien à rajouter à ce qu'on dit gbdivers et lmghs, car ils ont déjà tout dit ;)
          • 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
            12 octobre 2021 à 8:43:20

            Dedeun a écrit:

            Bonsoir,

            Dans le post précédent, Koala01 a dit:

            >  5- on ne met jamais new et delete dans une même fonction

            Pourquoi donc ?

            Si j'avais une variable locale, trop grosse pour être mise dans la pile, j'utiliserai une procédure avec un new (ou alors un constructeur de l'objet qui alloue la variable) au début, je ferais le calcule sur cette variable, et je la détruirai ensuite (ou alors j'appellerai le destructeur de l'objet, qui déléterait cet objet). Ce n'est pas comme ça qu'il est souhaitable de faire ?

            Il va falloir y aller pour faire sauter la pile avec une variable trop « grosse ». Dans tous les cas, tu n'auras jamais besoin de new ou delete sauf raison valable.

            http://klmr.me/slides/modern-cpp.pdf



            • Partager sur Facebook
            • Partager sur Twitter

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

              13 octobre 2021 à 18:46:57

              Bonsoir et merci pour vos réponses.

              • Ok pour la fuite mémoire, (RAII c'est mieux),
              • Ok pour les conteneurs de la STL.
              • Quant à l'occupation de la pile, "Heureux Homme es tu, si tu n'as pas à t'en préoccuper" !

              En fait, le message, c'est : "pas de new" et "pas de delete" tout court !

              Bien cordialement

              • Partager sur Facebook
              • Partager sur Twitter
                14 octobre 2021 à 1:41:45

                Dedeun a écrit:

                En fait, le message, c'est : "pas de new" et "pas de delete" tout court !

                Si possible...

                Maintenant, ben, quand tu as des types polymorphes à cause d'un héritage publique, que le type de tes instances est défini à  l'exécution et que tu veux gérer tes instances comme s'il s'agissait d'instances de la classe de base, l'idéal serait -- bien sur -- d'utiliser des pointeurs intelligents (qui, au final, vont cacher new et delete dans des fonctions -- voire des foncteurs -- séparés :D ) ou ... dans certaines circonstances... de faire tes propres classes RAII.

                A priori, tu as largement assez d'outils pour n'avoir aucun besoin d'appeler new et delete par toi-même.  Maintenant, sachant que rien n'est jamais gravé dans le marbre, va savoir si les a priori sont bons et valables dans ta situation particulière ? :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
                  14 octobre 2021 à 2:08:19

                  Pour new et delete, je ne vois pas trop de cas d'utilisation qui ne pourraient pas être remplacé par unique_ptr.

                  Mais on peut avec des cas d'utilisation avec malloc/free, des buffers non initialise 2, des fonctions systèmes ou de libs (en général pour des optimisations).

                  Et il ne faut pas oublier que l'initialisation ne concerne pas que l'allocation mémoire, mais également tout ce qui est nécessaire pour avoir un objet valide et tout ce qui est nécessaire pour libérer correctement.

                  Un article qui ne semble fondateur, c'est celui de Stroustrup sur la gestion des exceptions : https://www.stroustrup.com/except.pdf 

                  Le point super important dans l'implémentation du RAII, c'est de respecter le principe de responsabilité unique. Si tu essaies de faire trop de chose dans les constructeurs et destructeurs, tu peux perdre l'intérêt du RAII.

                  class MyFakeRAII {
                  public:
                      MyFakeRAII() {
                          m_p1 = new P;
                          m_p2 = new P; // peut lancer une exception !
                          doSomethingToInit(); // peut lancer une exception !
                      }
                      ~MyFakeRAII() {
                          doSomethingToRelease();
                          delete m_p1;
                          delete m_p2;
                      }
                  private:
                      P* m_p1 = nullptr;
                      P* m_p2 = nullptr;
                  };

                  Ce code n'est pas safe. Il ne faut pas hésiter à découper les classes.

                  class MyP1RAII {
                  public:
                      MyP1RAII() {
                          m_p1 = new P;
                      }
                      ~MyP1RAII() {
                          delete m_p1; // safe
                      }
                  };
                  
                  class MyP2RAII {
                  public:
                      MyP2RAII() {
                          m_p2 = new P;
                      }
                      ~MyP2RAII() {
                          delete m_p2; // safe
                      }
                  };
                  
                  class MyRealRAII {
                  public:
                      MyRealRAII() {
                          doSomethingToInit(); // safe
                      }
                      ~MyRealRAII() {
                          doSomethingToRelease(); // safe
                      }
                  private:
                      MyP1RAII p1;
                      MyP2RAII p2;
                  };

                  (Bien sur, dans ce code, MyP1RAII et MyP2RAII peuvent être remplacer par des unique_ptr. C'est pour illustrer)

                  -
                  Edité par gbdivers 14 octobre 2021 à 2:09:58

                  • Partager sur Facebook
                  • Partager sur Twitter

                  Fonction avec new et delete (suite)

                  × 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