Bonjour,
Reste une fuite de mémoire après la fin du programme?
Merci
Pointer ?
COBOL Programer
Bonjour,
Reste une fuite de mémoire après la fin du programme?
Merci
Pointer ?
COBOL Programer
Ca dépend ce qu'on appelle fuite, et ce qu'on appelle mémoire.
La valeur de retour de l'exécution d'un programme, c'est une fuite ou pas ?
Une mémoire partagée entre 2 processus, c'est de la mémoire ou pas ?
Fin de programme, c'est à la sortie de la fonction point d'entré du programme ou lorsque le Kernel récupère la mémoire du descripteur du processus ?
etc...
Pointer quoi ?
En plus c'est très variable, en fonction de l'OS, pas du langage C++.
Pouvez-vous être plus spécifique, SVP ?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 class Je{ int x; Je* nous; public: Je(int xx) : x{ xx } { nous = new Je(3); // ? } ~Je() { /* delete nous;*/ } int get_x(); Je& get_nous(); friend std::ostream& operator<<(std::ostream& os, Je&); }; std::ostream& operator<<(std::ostream& os, Je* nou) { os << "nous: " << nou->get_x() << std::endl; return os; } Je& Je::get_nous() { return *nous; } int Je::get_x() { return x; } int main() { Je *ich = new Je(5); std::cout << ich->get_x() << std::endl; //delete ich; ? //ou std::cout << ich->get_nous(); //? }
La fuite de données a-t-elle quelque chose à voir avec la RAM? Cela restera-t-il pour toujours? Heap?
Bonjour,
Une fuite mémoire est une perte pendant que l'application s'exécute. Dés que l'application se termine toute la mémoire utilisée (y compris celle "perdue") est rendue au système.
Ton programme a en effet bourré de fuites mémoires. A chaque new doit correspondre exactement un delete.
Il existe pourtant un moyen simple pour ne pas avoir de fuite, il suffit de ne jamais jamais utiliser new. Il existe des tas de moyens pour cela. Par exemple le code peut devenir:
Mais ce code, comme le tiens, va tenter de créer une infinité de Je. Car créer un Je demande à créer un Je qui demande à créer un Je qui demande à créer un Je qui ...
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 class Je { int x; std::unique_ptr<Je> nous; public: Je(int xx) : x{xx} { nous = std::make_unique<Je>(3); } ~Je() = default; int get_x()const; Je const& get_nous()const; friend std::ostream& operator<<(std::ostream&, Je const&); }; std::ostream& operator<<(std::ostream& os, Je const& nou) { os << "nous: " << nou->get_x() << std::endl; return os; } Je const& Je::get_nous()const { return *nous; } int Je::get_x()const { return x; } int main() { auto ich = std::make_unique<Je>(5); std::cout << ich->get_x() << std::endl; std::cout << ich->get_nous(); }
@dalfab simplifie un peu la chose.
Mais comme le PO semble un peu "un pied tendre" dans le domaine, il a peut-être raison de faire comme ça.
Dans un OS "moderne grand public", ce que fait décrit @dalfab est plutôt la norme.
Mais attention "fuite de mémoire" et "fuite de données" n'ont de rapport que de manière très très indirecte.
@Rimidalv, votre code, en plus d'être complètement bogué (cf. message de @dalfab) est un mélange de vieux code très archaïque (utilisation de pointeur nus) et de pratiques relativement récentes (initialisation via accolade).
Je vous conseille de n'utiliser que des sources qui n'utilisent que du codage moderne, pour ne pas avoir à gérer des problèmes qui ne le sont plus ou beaucoup moins présents avec les "nouvelles" pratiques.
Les cas les plus communs de fuite mémoire sont largement réduits en utilisant les smart-pointers, comme avec le code de @dalfab.
"fuite de mémoire" : moins lié à la RAM (composant physique), que lié à "l'encombrement" de l'espace d'adressage du processus, on peut très bien "encombrer" l'espace d'adressage sans utiliser plus de RAM (mémoires partagées, fichiers mappés en mémoire, etc...)
Au niveau mémoire vive, pas grand-chose ne résiste au Saint Redémarrage.Cela restera-t-il pour toujours?
Quand vous ne faites pas d'allocations "automatiques", oui c'est plutôt le (ou un, c'est paramétrable) tas (heap) qui est utilisé plutôt que la pile (stack).Heap?
En tant que programmeur aguerri (cf. COBOL Programer), vos questions semblent un peu candides.
Peut-être que des questions plus précises seraient plus "efficaces".
Merci &bacelar & @dalfab,
Merci, j’ai appris C ++ avec Thinking in C++ Volume II, et mes connaissances sont pleines de trous.
Bonjour,
Bravo pour avoir décidé d'apprendre le C++.
C'est un langage considéré comme difficile par certains, mais
cela vaut réellement le coup d'y consacrer des efforts.
Si j'ai bien compris, ta question principale porte sur le fait
que les fuites (de quoi ?) persistent ou non après la fin du programme
(voir tes 2 premiers posts).
Sur ce point, dalfab a bien expliqué que l'OS récupère toute la mémoire
alloué aux processus (et non libérée) après la terminaison de ceux-ci.
Cela ne veut pas dire que les programmes doivent tout laisser traîner derrière eux.
Mais ça n'épuise pas la question. Le problème concerne les ressources
en général. Par exemple, si un programme utilise comme ressource exclusive un périphérique
externe (carte d'acquisition de données "maison" par exemple), il est nécessaire que ce périphérique
soit "averti" que le programme est terminé. Sans cela, il peut rester verrouillé, ou
dans un état indéterminé qui impactera son fonctionnement ultérieur.
En ce sens, oui, une fuite peut persister.
Avant de se précipiter sur les pointeurs intelligents, il faut se demander si les bases sont acquises.
Par exemple, le mécanisme de construction/destruction est capital puisque la gestion des ressources
(pas seulement la mémoire) repose en définitive sur lui (RAII).
Je ne sais pas ce que devrait faire ton exemple, mais comme d'autres l'on dit,
il provoque une récursion infinie (voulue ? je ne sais pas) dans le constructeur, donc le programme
sera arrêté par une exception après avoir épuisé toute la mémoire que la machine voudra bien fournir.
Ceci est vrai même en restaurant le "delete nous;" en commentaire dans le destructeur, et ceci est également
vrai en utilisant des pointeurs intelligents (unique_ptr, shared_ptr, weak_ptr ou autres).
Ce qui adviendra de la mémoire déjà allouée dépendra alors de la présence d'un gestionnaire d'exception.
Le conseil d'utiliser des unique_ptr n'est pas mauvais dans l'absolu, mais tout à fait prématuré:
il est beaucoup plus important de commencer par apprendre à utiliser les pointeurs proprement.
Ceci permet de bien comprendre ce que sont les problèmes potentiels, par exemple les interactions avec les exceptions.
A ce sujet: non, les pointeurs nus ne sont pas "archaïques" (pas plus que int ou double).
Ils ont simplement des limitations/inconvénients (et avantages) qu'il faut connaître.
Pourquoi être dogmatique ?
Je reviens à unique_ptr : il faut bien comprendre quelle sorte de responsabilité il exerce sur les données
pointées (c'est assez facile) et aussi préciser que ce type de responsabilité ne convient pas à toutes les applications.
De plus, la responsabilité exclusive s'appuie sur les transmissions par déplacement (grâce à move),
et là, le concept est beaucoup moins accessible aux débutants.
Continuons sur le conseil d'utiliser make_unique : il est excellent.
Sauf qu'il débouche sur les concepts suivants: modèle variadique, reference aux rvalues et transmission parfaite.
C'est à peu près l'inventaire de tous les trucs les plus difficiles du C++ moderne.
Je défie un débutant de comprendre quelque chose au prototype de cette fonction, provenant de cppreference.com
(un site formidable que j'adore). Je cite :
template< class T, class... Args >
unique_ptr<T> make_unique( Args&&... args );
Bon, vous l'avez compris, je me suis lâché.
Ma réponse déborde largement de la question initiale, car c'est aussi une réponse aux réponses.
Répondre à un débutant qui s'interroge sur la persistance des fuites
"de toutes façon, il faut utiliser des pointeurs intelligents", ne me paraît pas adapté au public.
Merci, c’était exactement ce que je voulais savoir.
On discute toujours d'OS "modernes" ?Mais ça n'épuise pas la question. Le problème concerne les ressources
en général. Par exemple, si un programme utilise comme ressource exclusive un périphérique
externe (carte d'acquisition de données "maison" par exemple), il est nécessaire que ce périphérique
soit "averti" que le programme est terminé. Sans cela, il peut rester verrouillé, ou
dans un état indéterminé qui impactera son fonctionnement ultérieur.
En ce sens, oui, une fuite peut persister.
Parce que ce "problème" n'existe pas plus que les problèmes mémoire dans un OS comme Windows ou pour Unix (au moins pour les descripteurs de fichier "standard").
Quand l'OS "recycle" les ressources d'un processus, ces types de ressource, qui sont managés via des "handles/descripteur de fichier", sont libérées tout aussi automatiquement les ressources "mémoires".
Tout comme la mémoire, les "problèmes" arrivent quand on commence à faire dans le "non standard" : partage de handle "globale", partage de périphériques, partage de mémoire, gestion complexe des buffers réseaux, etc...
En bref, les OS modernes simplifient drastiquement la gestion de ces choses, dans les cas "standard", même quand l'application "explose en plein vol". C'est fondamental pour la fiabilité de l'OS et des programmes qui tournent "dedans".
@amorgos, en tant qu'enseignant-chercheur, vous devez avoir plus de recul sur l'enseignement de l'informatique que moi (mais mon esprit d'étudiant de 1er cycle du millénaire dernier n'est pas très d'accord avec cette assertion).
Mais je pense que pour un apprenant "naïf" du C++, mettre en exergue le concept d'ownership au moment de la présentation des pointeurs intelligents (fait avant les pointeurs nus) est important.
(dans mon message, le terme "naïf" correspond à sa définition en biologie : qui n'a pas encore été en contact avec virus, un ... => nouveau dans le développement informatique)
Les pointeurs nus ne devrait être utiles que dans des cas d'optimisation et des cas d'implémentation d'usage complexe, donc, pour moi, assez loin des préoccupations d'un apprenant "naïf" du C++.
Pour une personne venant du C ou d'autres langages de "bas-niveau", les pointeurs intelligents peuvent être déstabilisants, mais la simplicité de leur utilisation fiable par rapport aux pointeurs nus devrait être un "soulagement" pour ces "hommes aux mains velues".
Que l'implémentation et l'analyse syntaxique des pointeurs intelligents soit complexes, je n'en disconviens pas, mais l'utilisateur (pas l'implémenteur ni le concepteur de compilateur) n'a pas besoin de maîtriser tout le bazar.Je reviens à unique_ptr : il faut bien comprendre quelle sorte de responsabilité il exerce sur les données
pointées (c'est assez facile) et aussi préciser que ce type de responsabilité ne convient pas à toutes les applications.
De plus, la responsabilité exclusive s'appuie sur les transmissions par déplacement (grâce à move),
et là, le concept est beaucoup moins accessible aux débutants.
Continuons sur le conseil d'utiliser make_unique : il est excellent.
Sauf qu'il débouche sur les concepts suivants: modèle variadique, reference aux rvalues et transmission parfaite.
C'est à peu près l'inventaire de tous les trucs les plus difficiles du C++ moderne.
Je défie un débutant de comprendre quelque chose au prototype de cette fonction, provenant de cppreference.com
(un site formidable que j'adore). Je cite :
template< class T, class... Args >
unique_ptr<T> make_unique( Args&&... args );
Bon, vous l'avez compris, je me suis lâché.
(Bon, il y a les forcené de la "team bleu" qui ne comprennent qu'en ayant "tout démonté", mais généralement ils s'arrêtent avant d'avoir à comprendre les intrications quantiques liées à la proximité des atomes formant les différents transistors constituant des lignes de cache de niveau L1 ou L2 .)
Moi, j'ai du mal à cerner "le public" de la question.Ma réponse déborde largement de la question initiale, car c'est aussi une réponse aux réponses.
Répondre à un débutant qui s'interroge sur la persistance des fuites
"de toutes façon, il faut utiliser des pointeurs intelligents", ne me paraît pas adapté au public.
Je trouve que la réponse "vous prenez pas trop la tête, réfléchissez à l'ownership de votre bidule en mémoire et utilisez le "bon" type de smart-pointer en fonction de l'ownership, et laissez l'OS gérer", c'est pas mal et pas trop compliqué, quand on est "naïf".
Après seul @Rimidalv est capable de juger de la "simplicité" de l'approche, je pense.
Nous sommes d'accord. Remettons les chose dans le contexte: j'ai juste imaginé une situation dans laquelle le système ne serait pas capable de libérer la ressource après la fin du programme.
C'est la raison pour laquelle j'ai parlé de hardware "maison", qui est par essence non supporté par un driver, ou dont le driver ne respecte pas forcement les règles imposées par l'OS.
J'en ai conclu que oui, une fuite de ressource pouvait exister après la terminaison du programme (même si c'est heureusement très rare).
Mais c'est aussi ce que vous dites aussi, en creux, en parlant de problèmes qui arrivent lorsqu'on sort des cas standard.
Si vous voulez dire que vous avez eu des profs qui manquaient de recul (pour être gentil), je ne peux que confirmer. Ça arrive !
Alors là, je ne suis pas d'accord. Je crois que vous avez oublié vos débuts, ou vous avez eu la chance de comprendre rapidement ce qui était difficile pour les autres.
Pour avoir des chances de se faire comprendre, il vaut mieux ne pas expliquer deux choses à la fois.
La notion d'indirection est une des chose très difficiles à faire passer alors si vous rajoutez la notion d'ownership, ça ne vas pas faciliter les choses.
Pour moi, cela revient commencer la construction d'une maison par le toit (qui tient en l'air par un vague échafaudage),
sous le prétexte que c'est le toit qui protège de la pluie.
Et bien non: on commence par les fondations, et s'il pleut pendant la construction .. on se mouille. Mais au final, les fondations sont la garantie d'une maison solide.
Je sais que l'approche: "commençons par le haut, il sera toujours temps de descendre d'un niveau pour optimiser" est populaire.
Appliqué à l'enseignement (et peut-être pas que), cela donne une culture bourrée de trous car au final on ne descend jamais, soit par manque de temps, mais surtout par paresse intellectuelle.
L'archétype de l'approche est l'utilisation de Python en enseignement, qui est une catastrophe planétaire.
Je vous invite à aller sur un sujet récent du site, consacré à une comparaison Python 3.10, Python 3.11 et C++.
J'ai posté sur ce sujet et vous y verrez un code C++ absolument épouvantable écrit par un dev d'une (très) grande boîte américaine.
Ce code est écrit par un développeur Python (enfin, je le suppose), qui ne peut manifestement même pas imaginer que la mémoire puisse être gérée.
A ce sujet, on a souvent l'argument que délivrer le programmeur des aspects pénibles tels que la gestion de la mémoire lui permettrait de ses concentrer sur les
autres aspects, et en particulier sur les algos. Cet argument a l'air plein de bon sens, mais en pratique, un programmeur moyen restera moyen.
Si vous regardez de près l'exemple précité, vous y trouverez au moins trois erreurs de logique.
C'est la nouveauté, (quelle qu'elle soit) qui est déstabilisante.
Mais si la personne vient du C, elle sait normalement déjà ce qu''est un pointeur, et donc que du bonheur à rajouter la couche intelligente.
C'est largement vrai pour shared_ptr. Par contre, pour unique_ptr, il faut avoir compris les transmissions par déplacement, donc les références aux rvalue.
Absolument pas gagné pour un novice. Pour weak_ptr, il a aussi quelques trucs à comprendre avant de les utiliser correctement.
C'est tout de même un système assez complexe basé sur un double comptage de références et qui marche main dans la main avec shared_ptr.
Oui, mais c'est un peu la solution ceinture+bretelle+préservatif.
J'enlèverais "smart" de la phrase précédente. Vous partez du principe que les pointeurs servent uniquement à accéder à des données allouées dynamiquement.
Ce n'est pas vrai. Il existe des tas de situations où on a uniquement besoin de réaliser des indirections sur des données qui ne sont pas allouées dynamiquement
ou dont quelqu'un d'autre a la responsabilité. Dans ce cas, les pointeurs nus sont optimaux.
Sinon, on peut envisager un pointeur intelligent, mais savoir lequel nécessite d'avoir compris un minimum de choses sur leur fonctionnement.
Il est également possible d'utiliser les deux variantes. Bonne nuit.
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager