IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

Libération variable Null ou Delete


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé Avatar de LeonCosnyd
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    439
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2007
    Messages : 439
    Par défaut Libération variable Null ou Delete
    Bonjour,

    J'ai une question un peu bête mais je préfère avoir une réponse claire !

    J'utilise un pointeur que je déclare comme ceci :
    Dans mon code je l'initialise avec cette ligne :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    monObjet = new maClasse();
    J'ai besoin de réinitialiser mon pointeur pour pouvoir le réutiliser un peu plus loin ! Je me demande donc si je fais la bonne technique :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    monObjet = NULL;
    monObjet = new maClasse();
    Je me demande si la première allocation que j'ai fait à été libéré ? Ou dois-je faire un delete avant ? Mais si je delete monObjet le pointeur est-il toujours utilisable après ?

  2. #2
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 395
    Par défaut
    Normalement, tu dois faire un delete avant.

    Mais ça, c'est le vieux C++. Depuis C++11, tu peux faire ça à la place:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    unique_ptr<monObjet> = new maClasse();
    Et en C++14, la formule recommandée, c'est ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    unique_ptr<monObjet> = make_unique<maClasse>();
    Le unique_ptr<> se charge de la destruction de l'objet pointé.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  3. #3
    Membre éclairé Avatar de LeonCosnyd
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    439
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2007
    Messages : 439
    Par défaut
    unique_ptr<monObjet> = make_unique<maClasse>();
    Je ne savais même pas qu'il était possible de faire une gestion automatisée de la mémoire en C++ ! Car là il s'agit bien d'un fonctionnement similaire à un garbage collector (certes pour une variable) ???

    Merci pour l'info !

  4. #4
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 147
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 147
    Billets dans le blog
    4
    Par défaut
    Non il s'agit juste d'utiliser une classe et une variable membre, ainsi que la surcharge d'opérateur pour l'affectation, et profiter du mécanisme de destructeur appelé automatiquement quand la portée de la variable est dépassée.
    make_unique allant un peu plus loin en assurant d'être exception-proof, et peut-être d'autres subtilités
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  5. #5
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 395
    Par défaut
    Citation Envoyé par LeonCosnyd Voir le message
    Car là il s'agit bien d'un fonctionnement similaire à un garbage collector (certes pour une variable) ???
    Non, c'est juste un objet avec:
    1. Un destructeur qui fait un delete sur le pointeur contenu.
    2. Une sémantique de transfert faite correctement cette fois-ci (contrairement à auto_ptr où c'était fait maladroitement) grâce à une amélioration du langage lui-même.


    En fait, dans C++11, les trois améliorations majeures du langage lui-même sont:
    1. La sémantique de transfert supportée par le langage (les classes peuvent dorénavant avoir un constructeur de déplacement en plus du constructeur de copie).
    2. Les variadic templates (templates à nombres de paramètres variables; c'est ce qui permet de faire des wrappers "parfaits" de fonction, et c'est utilisé pour make_unique<> mais pas seulement)
    3. Les expressions lambda, qui ne sont pas utilisées dans le cadre de cette discussion mais ont leur utilité.

    Ensuite, la bibliothèque standard a reçu diverses améliorations, dont la plupart dépendent directement des améliorations du langage:
    • Le unique_ptr<>;
    • le template function<> qui harmonise et simplifie tout le système de foncteurs et pointeurs de fonction;
    • le support natif du multithreading via la classe thread;
    • le support natif de la génération de nombres aléatoires (je ne crois pas que celui-ci dépende d'une amélioration du langage)
    • etc.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  6. #6
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    En fait, les pointeurs intelligents sont ce qu'il convient d'appeler des "capsules RAII", car elles applique le (très mal nommé à mon sens) principe du même nom (RAII).

    Ce principe est en réalité l'acronyme (tu le sais surement, les développeurs adorent ce genre de choses ) de Ressource Acquisition Is Initialisation et part du principe que lorsque tu crées une données qui a besoin d'une ressource, le simple fait de créer ta donner doit s'assurer que la ressource dont elle a besoin sera bel et bien disponible. (Tu remarqueras au passage que je viens à peu près de t'expliquer le fonctionnement classique que l'on peut observer pour à peu près n'importe quel constructeur ). C'est grâce à ce principe que l'on peut depuis déjà bien longtemps avoir un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    int main(){
        std::ifstream ifs("fichier.txt");
        /* ici, le fichier a été ouvert et est utilisable, ou bien, ifs est dans un état invalide
         * et le test échoue
         */
        if(ifs){
            /* génial, on peut continuer */
       } 
    }
    Mais cet accronyme va plus loin, car, il y a toujours une sorte de "jeu en miroir" : si quelque chose se fait à la création, c'est sans doute qu'il y a "quelque chose" qui pourra le défaire à la destruction. Ainsi, le RAII stipule que toutes les ressources dont "quelque chose" est propriétaire devront être correctement détruite ... lorsque ce quelque chose est détruit.
    Modifions un peu le code que je viens de donner afin de comprendre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    int main(){
        if(someTest){
            std::ifstream ifs("fichier.txt");
            /* ici, le fichier a été ouvert et est utilisable, ou bien, ifs est dans un état invalide
             * et le test échoue
             */
            if(ifs){
                /* génial, on peut continuer */
        }
       } // ifs est automatiquement détruit ici
       return 0;
    }
    Le RAII nous donne la garantie que, lorsque ifs est détruit, les ressources matérielles que la classe std::ifstream utilise en interne seront correctment libérées. Dans le cas présent, nous aurons par exemple entre autre la certitude que le "lock" éventuellement placé par le système d'exploitation sur le fichier "fichier.txt" sera bel et bien levé, et ce, sans avoir quoi que ce soit à faire.

    L'énorme avantage, c'est que les développeurs ne valent souvent pas mieux que n'importe quel utilisateur, et c'est tout à fait normal : ce sont eux même des utilisateurs. Et, en tant que tel, il faut les considérer comme des "imbéciles distrait". Et ne crois pas que je me considère meilleur qu'un autre à ce sujet là (je ne suis d'ailleurs pas loin de penser que les meilleurs développeurs sont ceux qui sont conscients de cette faiblesse et qui mettent tout en oeuvre pour éviter les catastrophes qu'elle peut engendrer )

    Enfin, revenons à nos moutons : les pointeurs intelligents vont appliquer le même principe et s'assurer que la mémoire allouée au pointeur sous jascent sera effectivement libérée exactement au moment opportun, c'est à dire:
    • lorsque la variable de type std::unique_ptr sera détruite (parce qu'on sort de sa portée, ou parce qu'on la retire de la collection qui la contient)
    • lorsque le dernier std::shared_ptr référençant la mémoire allouée au pointeur sous-jascent sera détruit (parce que l'on sort de sa portée ou parce qu'on le retire de la collection qui la contient)

    Cela n'a donc absolument rien à voir avec un quelconque mécanisme de Garbage Collector: typiquement, le garbage collector va parcourir la liste des références qui existent pour savoir si elles sont encore utilisées, et ne supprimer que celles pour lesquelles ce n'est plus le cas; ce qui occasionne (allez, soyons sympa : qui peut occasionner) un "léger retard" (parfois même "pas si léger que ca" d'ailleurs) entre le moment où la référence à supprimer cesse d'être utile et le moment où elle est effectivement supprimée.

    Les pointeurs intelligents vont quant à eux occasionner la libération immédiate de la mémoire allouée au pointeur sous-jacent. Ainsi, si tu as un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    int main(){
       for(int i= 0;i<10;++i){
          auto ptr = std::make_unique<int>(i);
          std::cout<<*(ptr.get())<<"\n";
       } // on sort de la portée de ptr, ptr est donc détruit
       return 0;
    }
    lorsque ptr est détruit, la mémoire allouée au pointeur sous-jacent est directement libérée elle-aussi
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  7. #7
    Membre expérimenté
    Profil pro
    Développeur informatique
    Inscrit en
    Mai 2012
    Messages
    163
    Détails du profil
    Informations personnelles :
    Localisation : France, Côtes d'Armor (Bretagne)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mai 2012
    Messages : 163
    Par défaut
    Citation Envoyé par koala01 Voir le message
    En fait, les pointeurs intelligents sont ce qu'il convient d'appeler des "capsules RAII", car elles applique le (très mal nommé à mon sens) principe du même nom (RAII).
    Salut,

    Merci pour cette explication claire du RAII. Cependant, il me semble que le RAII et les pointeurs intelligents sont quand même assez orthogonaux.

    Le RAII permet de lier le cycle de vie d'une ressource à celle d'un objet (http://en.cppreference.com/w/cpp/language/raii). Ça correspond donc à la construction/destruction et ça s'utilise avec ou sans pointeur intelligent.

    Les pointeurs intelligents permettent de gérer la durée de vie des objets et ça s'applique "avec ou sans RAII à l'intérieur de l'objet".

    Maintenant, c'est vrai qu'on peut considérer qu'un attribut est déjà une ressource et qu'il y a donc toujours du RAII. De plus, effectivement, un pointeur intelligent va automatiquement lancer la destruction et donc indirectement terminer le RAII. C'est peut-être cela que tu appelles "capsule RAII" (cppreference parle de "RAII wrapper"). Mais le RAII existe également sans pointeur intelligent.

    Et pour les shared_ptr, ce n'est effectivement pas du "garbage collector" mais plutôt du "reference counting" déterministe (comme en python je crois).

  8. #8
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    Citation Envoyé par LeonCosnyd Voir le message
    Je ne savais même pas qu'il était possible de faire une gestion automatisée de la mémoire en C++ ! Car là il s'agit bien d'un fonctionnement similaire à un garbage collector (certes pour une variable) ???
    Contrairement aux autres réponses, je répondrais plutôt "oui" à cette question. Il s'agit bien là d'un mécanisme de type garbage collection, même s'il possède des caractéristiques assez différente des garbage collectors qui peuvent exister en Java et en C# par exemple. Les principales différences sont les suivantes :

    • Tu choisis pour chaque objet le mode de collection qui sera le plus approprié au moment de sa construction (géré sur la pile, dans un conteneur, par unique_ptr, par shared_ptr...), là où en Java/C#, il existe un mécanisme unique pour tous les objets (c'est un peu plus de travail, mais ça apporte des choses intéressantes en terme de perfs)
    • Ces mécanismes sont déterministes : On sait quand une ressource sera libérée, et on sait qu'elle sera libérée. Ce qui n'est pas le cas en Java/C#. Une conséquence est qu'on peut mettre du code intéressant dans les destructeurs, là où les finalizers sont globalement inutiles, et où d'autre mécanismes moins élégants ont dû être utilisés (les using du C#, les try-with-resources du Java)
    • Ces mécanismes sont à manier avec précaution quand on a des boucles, là ou un garbage collector classique gère les boucles sans soucis
    • Ces mécanismes sont plus économes en mémoire. Leur impact en performance est différent (il y a des cas où un garbage collector sera plus efficace, mais c'est assez rare).



    Régulièrement quand je dis "C++" à des développeurs Java, ils me répondent "ah, oui, le langage où il faut libérer la mémoire soi-même". Et ça a tendance à m'agacer, car ça montre que ces gens là critiquent non pas le C++ tel qu'il est (et pourtant, il y en aurait des critiques recevables !..), mais une image totalement faussée de ce langage, qui était peut-être vraie il y a 20 ans. Ça fait des années que dans du code à moi (par opposition à du code existant où je fais juste une micro-modification), je n'ai pas écrit un seul delete.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [MySQL] initialiser une variable à NULL
    Par papilou86 dans le forum PHP & Base de données
    Réponses: 3
    Dernier message: 11/07/2008, 12h01
  2. probleme variable null non acceptée
    Par soccard dans le forum VB 6 et antérieur
    Réponses: 2
    Dernier message: 28/05/2008, 18h06
  3. [Tableaux] Fonction extract() sur des variables NULL
    Par Tchupacabra dans le forum Langage
    Réponses: 7
    Dernier message: 21/05/2008, 22h34
  4. Pb récuperation Variable NULL
    Par cracov02 dans le forum DB2
    Réponses: 2
    Dernier message: 28/04/2008, 15h29
  5. Probleme variable null
    Par florentino dans le forum Servlets/JSP
    Réponses: 1
    Dernier message: 06/07/2007, 15h53

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo