Apprendre C++ avec Qt : Leon 12 Allocation dynamique
1 - Notion de porte.................................................................................................................. 2 2 - Notion de dure de stockage................................................................................................ 2 3 - Variables locales statiques .................................................................................................. 3 Principe gnral...............................................................................................................3 Le cas des fonctions membre...........................................................................................4 Variables membre statiques vs. variables locales statiques d'une fonction membre .........5 4 - Allocation dynamique.......................................................................................................... 5 Rserver de la mmoire avec new.....................................................................................5 Initialiser une zone mmoire rserve avec new................................................................6 Librer la mmoire avec del et e ......................................................................................6 Les fuites de mmoire......................................................................................................8 A quoi sert rellement l'allocation dynamique ? ...............................................................8 5 - Bon, c'est gentil tout a, mais a fait dj 7 pages. Qu'est-ce que je dois vraiment en retenir ?.............................................................................................................................. 9
Document du 22/08/05 - Retrouvez la version la plus rcente sur http://www.up.univ-mrs.fr/wcpp C++ - Leon 12 Allocation dynamique 2/9 Les objets que nous avons manipuls jusqu' prsent sont des variables locales au bloc l'intrieur duquel elles sont dfinies. Avant d'introduire la possibilit de crer des objets d'un autre type, prenons quelques instants pour prciser deux notions qui vont s'avrer importantes pour bien comprendre en quoi ces nouveaux objets diffrent de ceux que nous connaissons dj. 1 - Notion de porte On appelle porte d'une variable (ou d'une fonction) la portion du programme dans laquelle il est possible d'accder la variable (ou la fonction) en utilisant son nom.
Le terme anglais dsignant la porte est scope.
Il faut remarquer que le fait que le nom d'une variable ne permette pas d'accder celle-ci n'implique pas ncessairement que la variable ait cess d'exister. Une simple homonymie entre variables permet de crer une situation illustrant ce fait. Dans l'exemple suivant, nous avons simplement deux blocs de code, dont l'un est inclus dans l'autre. Nous savons que, dans ce cas, les variables locales du bloc externe sont disponibles l'intrieur du bloc interne.
Oui, nous le savons. Comment pourrait-on utiliser les structures de contrle (les f or , whi l e et autres i f ), si les blocs dont ces structures contrlent l'excution ne pouvaient accder qu' leurs propres variables locales ?
{ 1 i nt t est = 4; / / df i ni t i on d' une pr emi r e var i abl e nomme t est 2 { 3 t est = 5; / / changement de l a val eur cont enue dans l a pr emi r e var i abl e 4 i nt t est = 3; / / df i ni t i on d' une seconde var i abl e nomme t est 5 / / i l est devenu i mpossi bl e d' accder l a var i abl e qui cont i ent 5 6 }/ / l a var i abl e qui cont i ent 3 cesse d' exi st er i ci 7 / / i l est nouveau possi bl e d' accder l a var i abl e qui cont i ent 5 8 } 9
Ce qui rend cette situation particulire, c'est que les blocs possdent tous les deux une variable locale nomme t est . Une fois dfinie, la variable du bloc interne masque la variable homonyme dfinie dans le bloc externe, ce qui signifie que celle-ci se retrouve hors de porte (on ne peut plus y accder en utilisant son nom). Ds l'accolade marquant la fin du bloc interne (7), la variable locale de celui-ci cesse d'exister. Le phnomne de masquage disparat donc, et la variable locale du bloc externe redevient accessible. Elle a mme conserv son contenu, ce qui prouve bien qu'elle n'a jamais cess d'exister.
Le fragment de code prsent ci-dessus n'est destin qu' mettre en vidence la diffrence entre porte et existence. Il ne constitue certainement pas un exemple stylistique qu'il serait judicieux de suivre. En effet, s'il est parfois lgitime de dfinir une variable qui en masque une autre, il serait de trs mauvais got de le faire dans un bloc qui manipule galement la variable masque. Si un mme nom peut ventuellement dsigner des choses diffrentes diffrents moments, il est prfrable de conserver une correspondance entre "moments" et "blocs de code", faute de quoi la lisibilit du programme risque d'tre compromise. 2 - Notion de dure de stockage Lorsqu'on dit qu'une variable "cesse d'exister", cela signifie que la zone de mmoire dont l'tat reprsentait le contenu de la variable cesse d'tre rserve cet usage. Toutes les variables cessent videmment d'exister lorsque l'excution du programme s'achve, et nous savons aussi que les variables locales cessent d'exister lorsque s'achve l'excution du bloc de code l'intrieur duquel elles sont dfinies.
Lorsque la variable qui cesse d'exister est une instance d'une classe, ses variables membre cessent galement d'exister (elles ne sont que des sous-variables de l'instance).
Le fait qu'une variable cesse d'exister n'a pas forcment pour consquence une modification immdiate de l'tat de la zone de mmoire qui lui tait rserve, mais plus rien ne garantit que cet tat ne variera que d'une faon cohrente avec la signification qu'avait la variable. Il devient donc extrmement prilleux d'interprter cet tat comme s'il correspondait encore au contenu de la variable, et le langage, trs logiquement, interdit alors d'utiliser le nom de la variable. J-L Pris - 22/08/05 C++ - Leon 12 Allocation dynamique 3/9 Remarquons tout de suite que, si l'on accde la zone de mmoire qui correspondait la variable en utilisant un pointeur contenant son adresse, le langage N'EST PAS en mesure de proposer une scurit analogue celle offerte par l'interdiction de l'usage du nom de la variable. Cette situation est illustre dans l'exemple suivant :
{ 1 i nt uneVar i abl e; 2 i nt *poi nt eur = & uneVar i abl e; 3 *poi nt eur = 5; / / OK, on r ange 5 dans uneVar i abl e, en f ai t 4 { 5 i nt uneVar i abl eLocal e; 6 poi nt eur = & uneVar i abl eLocal e; 7 *poi nt eur = 5; / / OK, on r ange 5 dans uneVar i abl eLocal e, en f ai t 8 }/ / uneVar i abl eLocal e cesse d' exi st er ! 9 *poi nt eur = 5; / / DANGER : on modi f i e une zone de mmoi r e dont on i gnor e t out 10 } 11
Ce danger est insidieux, car il est fort possible que le programme ne prsente aucun symptme immdiat. Tant que la zone de mmoire qui tait attribue uneVar i abl eLocal e n'est pas affecte un autre usage, tout semble en effet se passer normalement. Le problme peut n'apparatre que bien plus tard au cours du dveloppement du programme et, qui plus est, il risque fort de se manifester par l'apparition de valeurs fantaisistes dans une variable qui n'a aucun rapport avec la ligne de code qui contient l'erreur. Les erreurs de ce type sont donc trs difficiles dtecter et il faut tout prix essayer d'viter de les commettre.
Lorsqu'une variable cesse d'exister, tous les pointeurs qui contiennent son adresse cessent d'tre valides et constituent autant de menaces graves pour l'intgrit du programme.
En ralit, les vrais problmes n'apparaissent que dans des situations bien plus complexes que celle illustre ci-dessus. Quoi qu'en disent certains esprits chagrins, tant que vous comprenez rellement ce que vous faites, l'usage de pointeurs n'est pas plus dangereux que n'importe quelle autre technique de programmation.
Et, si vous ne comprenez pas rellement ce que vous faites, toutes les techniques de programmation s'avrent finalement trop dangereuses. 3 - Variables locales statiques Une variable locale "ordinaire" cesse d'exister la fin de l'excution du bloc dans lequel elle est dfinie. Sa dure de stockage correspond donc exactement sa porte : elle nat au moment de l'excution de l'instruction qui la dfini, meurt la fin du bloc, et seules les instructions figurant entre ces deux points peuvent y accder en utilisant son nom.
Si un objet portant un nom identique est dfini ailleurs, il ne s'agit du mme nom que d'un point de vue purement orthographique. Du point de vue de C++, ces deux noms sont aussi diffrents que s'ils n'avaient pas une seule lettre en commun.
Chaque fois qu'un bloc est excut, ses variables locales sont recres et, ventuellement, r- initialises. Dans le cas d'une fonction, il arrive que cette caractristique des variables locales ne permette pas d'obtenir l'effet recherch. Principe gnral Imaginons une fonction qui a, pour une raison quelconque, besoin de tenir compte du nombre de fois o elle a dj t appele. Une variable locale ordinaire ne peut servir tenir ce compte, puisque son contenu est perdu la fin de chacune des excutions de la fonction. D'un autre ct, si cette fonction est appele partir de plusieurs parties diffrentes du programme, il peut s'avrer assez complexe de lui passer, lors de chaque appel, l'adresse d'une mme variable dans laquelle elle pourrait tenir jour le dnombrement des appels.
La difficult provient du fait que les diffrentes parties du programme n'ont pas forcment, elles- mmes, la possibilit de toutes accder une mme variable. Cette approche aurait, en outre, l'inconvnient de crer une variable dont la porte dpasserait largement la zone du programme o elle est effectivement utile, qui est limite la fonction. Un tel dpassement est viter, car il augmente le risque que la variable soit modifie par un programmeur n'ayant pas parfaitement conscience de sa signification et de la faon dont elle doit tre manipule. J-L Pris - 22/08/05 C++ - Leon 12 Allocation dynamique 4/9 Il existe un moyen simple pour rsoudre ce problme : crer des variables locales dont la dure de stockage n'est pas limite l'excution de la fonction. Pour crer ce type de variables, il suffit, lors de leur dfinition, de faire prcder leur type du mot st at i c.
Les variables locales st at i c naissent (et sont initialises) lors de la premire excution du bloc o elles sont dfinies. Elles ont la porte habituelle mais ne meurent qu' la fin du programme.
Le fragment de code suivant dfinit une fonction dont les appels successifs produisent progressivement la suite des nombres positifs impairs :
i nt gener at eur DI mpai r s( ) 1 { 2 st at i c i nt i mpai r Sui vant = - 1; / / df i ni t i on et i ni t i al i sat i on 3 i mpai r Sui vant = i mpai r Sui vant + 2; 4 r et ur n i mpai r Sui vant ; 5 } 6
Remarquez qu'une telle fonction ne pourrait pas tre crite s'il n'existait pas un processus d'initialisation distinct de celui de l'affectation. Remarquez galement que, l'usage, ce genre de fonction peut rserver quelques surprises :
i nt n; f or ( n=0 ; n < 5 ; ++n) maFonct i on( gener at eur DI mpai r s( ) ) ;
Ce fragment de code semble invoquer 5 fois maFonct i on( ) , en lui passant successivement les valeurs 1, 3, 5, 7 et 9. Mais que se passe-t-il si quelqu'un qui n'est pas conscient de ce contexte d'utilisation introduit un appel gener at eur DI mpai r s( ) dans le corps de maFonct i on( ) ? Le cas des fonctions membre L'utilisation de variables statiques locales une fonction membre ncessite d'tre conscient du fait qu'il n'existe pas un "exemplaire" de chaque fonction membre pour chaque instance de la classe. En clair, quelle que soit l'instance utilise pour appeler la fonction membre, c'est le mme jeu de variables locales statiques qui sera utilis.
Dans bien des cas, l'usage d'une variable membre s'avre plus judicieux que celui d'une variable statique locale une fonction membre, car il permet chacune des instances de maintenir des valeurs diffrentes.
Une classe "fonctionode" dote d'une variable membre permet, par exemple, de traiter correctement le problme de notre gnrateur de nombres impairs :
cl ass gener at eur DI mpai r s { publ i c: gener at eur DI mpai r s( ) : m_val eur ( - 1) {} i nt oper at or ( ) ( ) {r et ur n m_val eur += 2; } pr ot ect ed: i nt m_val eur ; };
L'usage de cette classe garantit que, quoi qu'entreprenne la fonction appele, elle ne peut en aucun cas interfrer avec le gnrateur utilis par la fonction appelante (puisqu'elle n'y a pas accs). Nous pouvons donc crire en toute scurit :
gener at eur DI mpai r s i mpai r Sui vant ; i nt n; f or ( n=0 ; n < 5 ; ++n) maFonct i on( i mpai r Sui vant ( ) ) ;
Il arrive que l'unicit des variables locales statiques d'une fonction membre ne pose pas problme, soit parce que c'est prcisment l'effet souhait, soit parce que la classe n'est pas destine tre instancie plusieurs fois simultanment 1 (ce qui est, par exemple, le cas de la plupart des classes dcrivant un dialogue avec l'utilisateur...). Le recours des variables locales statiques prsente alors le double avantage de limiter le nombre de variables membre et de ne pas rendre l'information accessible aux autres fonctions membre.
1 Si une classe ne supporte pas d'tre instancie plusieurs fois simultanment, il est de bonne politique de rendre ceci impossible (en pratiquant, par exemple, un comptage d'occurrences tel que celui dcrit dans l'Annexe 3). J-L Pris - 22/08/05 C++ - Leon 12 Allocation dynamique 5/9 Variables membre statiques vs. variables locales statiques d'une fonction membre Les lecteurs qui ont parcouru l'Annexe 3 savent que le mot st at i c peut galement qualifier des variables membres, ce qui peut prter confusion. En rsum :
Si une classe possde une variable membre statique, toutes les fonctions membre y ont accs (puisqu'il s'agit d'une variable membre) et elles accderont toujours la mme variable, quelle que soit l'instance au titre de laquelle elles sont excutes (puisque la variable est statique).
Si une fonction membre possde une variable locale statique, elle seule y aura accs (puisqu'il s'agit d'une variable locale) et elle accdera toujours la mme variable, quelle que soit l'instance au titre de laquelle elle est excute. 4 - Allocation dynamique Rendre une variable locale statique est donc un moyen d'augmenter sa dure de stockage, sans modifier pour autant sa porte. Ce moyen reste toutefois assez rudimentaire, car il n'offre de contrle prcis ni sur le moment de la naissance de la variable (c'est forcment lors de la premire excution du bloc) ni sur le moment de sa disparition (une variable statique ne disparat qu' la fin de l'excution du programme). Lorsqu'une meilleure prcision dans le contrle de la dure de stockage s'avre ncessaire, c'est au processus d'allocation dynamique de la mmoire qu'il va nous falloir avoir recours.
La premire chose qu'il faut bien comprendre propos de l'allocation dynamique est que les objets ainsi crs restent anonymes.
Selon la dfinition que nous avons adopte pour ce mot, ce ne sont donc pas des variables.
En l'absence de nom permettant de les dsigner, ces objets ne peuvent donc tre manipuls qu' l'aide de pointeurs contenant leur adresse ou de rfrences leur tant associes. Rserver de la mmoire avec new Jusqu' prsent, la seule mthode dont nous disposions pour rendre un pointeur valide tait d'utiliser l'oprateur "adresse de" pour obtenir l'adresse d'une variable du type adquat :
doubl e uneVar = 3. 14; 1 doubl e * pDoubl e = &uneVar ; 2
L'allocation dynamique fournit un moyen permettant de rserver une zone de mmoire d'une taille correspondant au type de donne que l'on souhaite y stocker.
Il n'y a aucune restriction sur le type de donnes pour lesquelles newpeut rserver une zone de stockage. Les types natifs ne bnficient d'aucun privilge de ce point de vue par rapport aux types que vous avez crs vous-mme (en dfinissant une classe, par exemple).
L'oprateur qui effectue cette rservation est not new, et il faut, bien entendu, lui prciser le type de donne qui doit pouvoir tre stock dans l'emplacement rclam. Si une zone de mmoire de la taille ncessaire est disponible, l'oprateur new la marque comme tant rserve et produit comme rsultat l'adresse de cette zone. Si la rservation de mmoire choue (lorsque, par exemple, toute la mmoire de l'ordinateur qui excute le programme est dj utilise), l'oprateur new donne un rsultat NULL.
C'est en tout cas la mthode qu'utilisait "traditionnellement" newpour signaler le problme. Si vous disposez d'un compilateur "moderne", il est possible qu'il utilise par dfaut le mcanisme des exceptions (cf. Leon 23). Vous pouvez alors lui demander explicitement de s'en tenir sa premire mthode en utilisant new( not hr ow) en lieu et place de new.
Il convient donc, avant tout usage d'une adresse obtenue grce new, de vrifier que cette adresse est valide. Si ce n'est pas le cas, le programme doit adopter un comportement adapt.
Dans le cas de programmes simples, l'chec d'une demande d'allocation de mmoire doit, au minimum, conduire un arrt de l'excution du programme, prcd d'un message avertissant l'utilisateur de la nature du problme. La faon correcte de mettre fin l'excution du programme ne peut pas tre dcrite ici, car elle dpend du systme utilis. Dans le cadre de projets plus ambitieux, il est parfois possible de mettre en place des stratgies palliatives, pour viter d'avoir abandonner prmaturment l'excution du programme.
J-L Pris - 22/08/05 C++ - Leon 12 Allocation dynamique 6/9 Le fragment de code suivant propose un exemple de mise en uvre de ce systme :
doubl e * pDoubl e = NULL; 1 pDoubl e = new doubl e; 2 i f ( pDoubl e ! = NULL) 3 { / / on peut ut i l i ser *pDoubl e comme s' i l s' agi ssai t du 4 *pDoubl e = 3. 14; / / nomd' une var i abl e de t ype doubl e 5 } 6 el se 7 / / i l f aut pr endr e des mesur es ner gi ques ! 8
Dans cet exemple, une variable de type "pointeur sur doubl e" est tout d'abord cre (1). Cette variable est initialise avec la constante NULL, qui indique clairement que la variable ne contient pas, pour l'instant, l'adresse d'un doubl e (et qu'il faut donc se garder de toute tentative de drfrencement).
Dans un second temps, l'oprateur new est utilis pour rclamer l'adresse d'une zone mmoire susceptible de stocker une valeur de type doubl e. L'adresse produite par new est range dans la variable pDoubl e (2).
L'oprateur newproduit une adresse. C'est donc bien dans une variable de type pointeur qu'il convient de stocker ce rsultat. De toutes faons, essayer de drfrencer pDoubl e avant de lui avoir donn un contenu valide ne pourrait conduire qu' une catastrophe. Il serait donc doublement mal venu d'crire :
doubl e * pDoubl e = NULL; * pDoubl e = new doubl e; / / une er r eur gr ossi r e !
En effet, drfrencer le pointeur nous conduit accder la zone de mmoire qu'il dsigne. Or pDoubl e est de type "pointeur sur doubl e", ce qui signifie que la zone qu'il dsigne est destine stocker une valeur dcimale, et non une adresse. De plus, comme pDoubl e n'est pas valide, la valeur produite par newserait copie en mmoire un endroit imprvisible et vraisemblablement utilis d'autres fins.
La suite du programme dpend de la valeur de pDoubl e. Si new a effectivement renvoy une adresse valide, nous disposons d'une zone de mmoire qui peut contenir une valeur de type double et laquelle nous accdons (5) en drfrenant pDoubl e. Dans le cas contraire, il ne faut surtout pas essayer de drfrencer pDoubl e !
Il est galement possible (mais moins habituel) d'utiliser une rfrence pour dsigner l'objet cr dynamiquement :
doubl e & unDoubl e = *new doubl e; i f ( &unDoubl e ! = NULL) { / / on peut ut i l i ser unDoubl e comme s' i l s' agi ssai t du unDoubl e = 3. 14; / / nomd' une var i abl e de t ype doubl e } el se / / i l f aut pr endr e des mesur es ner gi ques !
Remarquez que, pour "initialiser" la rfrence, il faut bien entendu drfrencer l'adresse renvoye par new. Inversement, pour tester la validit de la rfrence, c'est l'adresse de l'objet qu'elle dsigne qui doit tre vrifie. En contrepartie de ces efforts, nous obtenons la possibilit de dsigner l'objet cr sans avoir utiliser le moindre oprateur. Initialiser une zone mmoire rserve avec new La demande de rservation d'une zone mmoire peut tre assortie d'une demande d'initialisation de cette zone. La syntaxe employe dans ce cas est l'une de celles possibles pour initialiser les variables : il suffit de mentionner entre parenthses la valeur souhaite :
i nt *pEnt i er = NULL; 1 pEnt i er = new i nt ( 12) ; / / i ni t i al i sat i on de l a zone avec l a val eur 12 2 Librer la mmoire avec del et e Si elle n'est pas explicitement libre, la mmoire rserve l'aide de l'oprateur new reste rserve jusqu' la fin de l'excution du programme. L'oprateur qui permet cette libration se nomme del et e, et il faut, bien entendu, lui indiquer quel est le bloc de mmoire qui doit tre J-L Pris - 22/08/05 C++ - Leon 12 Allocation dynamique 7/9 remis la disposition du systme. Assez naturellement, on dsigne le bloc de mmoire librer en indiquant son adresse. Si l'on reprend l'exemple prcdent, la libration de la mmoire se prsenterait de la faon suivante :
doubl e * pDoubl e = NULL; 1 pDoubl e = new doubl e; 2 i f ( pDoubl e ! = NULL) 3 { 4 *pDoubl e = 3. 14; / / on peut ut i l i ser *pDoubl e comme s' i l s' agi ssai t du nom 5 / / d' une var i abl e de t ype doubl e. 6 del et e pDoubl e; / / Lor squ' on n' en a pl us besoi n, on l i br e l a mmoi r e. 7 } 8 el se 9 / / i l f aut pr endr e des mesur es ner gi ques ! 10
La notation employe ne doit pas vous conduire un contresens concernant l'effet de del et e pDoubl e;
Contrairement ce qu'on pourrait croire, l'excution de cette instruction n'a aucun effet sur pDoubl e (qui continue exister), ni mme sur le contenu de cette variable (qui peut ventuellement continuer tre l'adresse du bloc de mmoire qui vient d'tre libr). Le seul aspect de la variable pDoubl e qui soit certainement affect par l'excution de l'instruction en question est sa validit en tant que pointeur. Aprs cette instruction, il devient videmment dangereux de drfrencer pDoubl e, puisque ceci revient accder une zone de mmoire dont nous ignorons dsormais quel est l'usage qui en est fait par le systme.
Cette situation est trs proche de celle signale page 3, propos du pointeur contenant l'adresse d'une variable locale un bloc venant de se refermer. La disparition de la variable locale libre la mmoire qui lui tait alloue, exactement comme del et e libre le bloc de mmoire dont l'adresse lui est passe. Dans les deux cas, il nous reste un pointeur contenant l'adresse d'une zone de mmoire dont l'usage nous est dsormais inconnu, c'est dire un pointeur invalide.
Etant donn que del et e a besoin de l'adresse du bloc librer, cet oprateur ne peut tre utilis que dans la mesure o il existe une variable de type pointeur contenant l'adresse en question. Il n'est pas pour autant ncessaire que le pointeur utilis cette occasion soit le mme que celui ayant initialement reu l'adresse produite par new. Il arrive assez frquemment que ce ne soit pas le cas, comme dans l'exemple suivant, o une fonction ut i l i sat r i ce( ) sous-traite al l oueI nt ( ) la tche consistant allouer la mmoire et mettre fin au programme en cas d'chec de l'allocation.
i nt * al l oueI nt ( ) 1 { 2 i nt * pEnt i er = new i nt ; 3 i f ( pEnt i er == NULL) 4 { 5 / / i ci doi vent f i gur er des i nst r uct i ons dest i nes met t r e f i n au pr ogr amme } 6 r et ur n pEnt i er ; 7 } 8 / / *************************************************** voi d ut i l i sat r i ce( ) 9 { 10 / / appel s de l a f onct i on sous- t r ai t ant e i nt * unPoi nt eur = al l oueI nt ( ) ; 11 i nt * unAut r e = al l oueI nt ( ) ; 12 / / i ci peut f i gur er du code ut i l i sant *unPoi nt eur et *unAut r e 13 / / l i br at i on de l a mmoi r e 14 del et e unPoi nt eur ; 15 del et e unAut r e; 16 } 17
Dans cet exemple, la seule utilisation de new figure (3) dans la fonction al l oueI nt ( ) , et elle rserve une nouvelle zone de mmoire chaque appel de la fonction. Ces zones mmoire ne sont pas destines tre utilises par al l oueI nt ( ) , mais par la fonction qui l'appelle. La fonction al l oueI nt ( ) s'achve donc (7) en renvoyant comme rsultat l'adresse du bloc qui J-L Pris - 22/08/05 C++ - Leon 12 Allocation dynamique 8/9 vient d'tre rserv (sauf, bien entendu, si l'allocation choue). Remarquez bien que la fonction retourne la valeur du pointeur, et non la valeur contenue dans la zone de mmoire dont l'adresse est dans ce pointeur. L'instruction r et ur n est donc applique simplement sur la variable pEnt i er , sans opration de drfrencement.
L'adresse renvoye par al l oueI nt ( ) est stocke par la fonction ut i l i sat r i ce( ) dans l'une de ses variables locales (11-12). Lorsque la fonction utilisatrice s'achve, elle libre tous les blocs de mmoire allous dynamiquement en appliquant l'oprateur del et e chacun de ses pointeurs (15-16). L'quilibre entre allocation et libration est donc obtenu ici avec un seul new (3) contre deux del et e (15 et 16), chacune de ces trois instructions impliquant un pointeur diffrent (pEnt i er , unPoi nt eur et unAut r e).
Le fait qu'appliquer del et e un pointeur affecte la disponibilit de la zone de mmoire dsigne et non le pointeur lui-mme ne signifie pas seulement que la zone de mmoire peut tre libre en la dsignant l'aide d'un autre pointeur que celui ayant recueilli son adresse lors de l'allocation. Il signifie aussi que, si plusieurs pointeurs contiennent la mme adresse, un seul del et e (portant sur n'importe lequel d'entre eux) les rendra tous invalides.
Lorsqu'un bloc de mmoire est libr l'aide de del et e, TOUS les pointeurs qui contiennent son adresse deviennent invalides. Les fuites de mmoire Si un programme procde de nombreuses allocations dynamiques sans prendre soin de librer les zones de mmoire devenues inutiles, la quantit de mmoire disponible finit fatalement par tre puise, ce qui se traduit par l'chec d'une des demandes d'allocation.
Mme si l'usage que fait votre programme de la mmoire est drisoire vis vis de la capacit mmoire totale de la machine que vous utilisez, laisser la fin du programme librer pour vous la mmoire que vous avez rquisitionne l'aide de new est une pratique de sauvage.
Que va-t-il se passer si votre programme est excut sur un ordinateur moins richement dot en mmoire ? Et si de trs nombreux programmes diffrents sont simultanment excuts ? Et si plusieurs exemplaires de votre programme sont excuts en mme temps ? Et si quelqu'un d'autre (ou vous-mme, dans six mois) essaie de gnraliser votre programme et lui donne une ampleur que vous n'aviez pas prvue ? Mme si rien de tout cela ne se produit, il est clair que, dans un programme bien conu, il est assez facile de librer proprement tous les blocs allous avec new. Si votre programme ne le fait pas, cela en dit long sur la qualit gnrale de sa conception et suggre que d'autres erreurs pourraient bien y tre tapies.
A chaque allocation d'un bloc de mmoire l'aide de l'oprateur new doit correspondre une libration du bloc en question l'aide de l'oprateur del et e.
Lorsqu'un programme ne respecte pas cette rgle, on dit qu'il prsente une "fuite de mmoire" (memory leak, en anglais), et il s'agit d'un indice peu prs aussi rassurant qu'une flaque d'huile sous la voiture d'occasion que vous vous apprtez acheter. A quoi sert rellement l'allocation dynamique ? D'une faon gnrale, l'allocation dynamique s'avre indispensable ds qu'un nombre important ou imprvisible de donnes doivent tre manipules.
La plupart du temps, vous n'utiliserez pas directement les oprateurs new et del et e mais des classes conteneur (QVal ueLi st ou QMap, par exemple) qui assurent moindre frais une gestion correcte de l'allocation dynamique.
Il reste nanmoins deux cas o le recours explicite l'allocation dynamique s'imposera :
- lorsque certains widgets proposs l'utilisateur seront mis en place par le programme lui- mme, au cours de son excution (et non prpositionns l'aide de Qt Designer).
Les widgets gnrs dynamiquement sont habituellement confis un objet Qt prexistant, qui en prend la responsabilit : la destruction du widget est alors assure automatiquement en temps voulu, et aucun appel explicite del et e ne doit tre fait.
- lorsque vous utiliserez des structures de donnes autres que celles prises en charge par les conteneurs Qt (cf. Leons 17, 18, 19, 20 et 21) ; J-L Pris - 22/08/05 C++ - Leon 12 Allocation dynamique 9/9 5 - Bon, c'est gentil tout a, mais a fait dj 7 pages. Qu'est-ce que je dois vraiment en retenir ? 1) La porte d'une variable est la portion du programme o le nom de la variable peut tre utilis.
2) En gnral, la dure de stockage d'une variable correspond sa porte : de la dfinition de la variable jusqu' la fin du bloc o cette dfinition figure.
3) Lorsque, l'intrieur de la porte d'une variable, on dfinit une variable homonyme, cette nouvelle variable masque la premire (qui se trouve donc hors de porte, jusqu' la disparition de la seconde variable).
4) Rendre une variable statique prolonge sa dure de stockage jusqu' la fin de l'excution du programme, sans modifier sa porte.
5) On peut obtenir une zone de stockage convenant une donne d'un type quelconque en utilisant l'oprateur new.
6) L'oprateur new produit l'adresse de la zone de mmoire rserve, et cette adresse peut tre stocke dans un pointeur du type adquat.
7) Si new ne parvient pas rserver la zone de mmoire demande, l'adresse qu'il produit est NULL.
8) Les blocs de mmoire rservs l'aide de new doivent tre librs l'aide de del et e.
9) Drfrencer un pointeur contenant l'adresse d'une zone de mmoire qui n'est pas (ou plus) consacre au stockage d'une donne d'un type correspondant celui du pointeur est rarement une bonne ide.