Bonjour,
J'aimerai clarifier un point avec les records et new.
Est-ce que l'on doit aussi utiliser un block try ... finally
avec new
Merci d'avance
Bonjour,
J'aimerai clarifier un point avec les records et new.
Est-ce que l'on doit aussi utiliser un block try ... finally
avec new
Merci d'avance
NON
Ta réponse me parait un peu directive ALWEBER !
Tout dépend de ce que tu entends pas là. Moi je dirai oui si tu fais une analogie avec le create :
devient pour un record
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 x := TObject.Create; try ... finally x.Free; end;
... si telle est la question
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 new(x); try ... finally Dispose(x); end;
Je prend comme exemple au hasard un élément d'un projet que je migre à partir d'un projet existant
Cela signifie que le précédent programmeur n'a pas anticipé la mauvaise exécution de sa procédure "LoadFromRT2" qui en fait devrait être une fonction.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9 RTEditor := TRTEditor.Create(nil); try RTEditor.Modele := MainManager.Modele; RTEditor.LoadFromRT2(f_calcul.GetCalcDir + '/calc.rt2'); RTEditor.ShowForm(True); finally FreeAndNil(RTEditor); end;
Un code correct devrait avoir plutôt cette forme :
Donc c'est une affirmation au regard d'une longue expérience puisque je programme en Pascal et ses descendants depuis 1981.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 RTEditor := TRTEditor.Create(nil); RTEditor.Modele := MainManager.Modele; if RTEditor.LoadFromRT2(f_calcul.GetCalcDir + '/calc.rt2') then RTEditor.ShowForm(True) Else ... FreeAndNil(RTEditor);
Et cette affirmation est vraie sur au moins 80% des cas rencontrés.
Reste 20%. Ce "non" est clairement trop catégorique
Dans ton exemple, tu considère que f_calcul est assigné. Pourquoi ne pas le tester aussi pour en être sûr ? C'est une source d'erreur potentielle (et que LoadFromRT2 soit une fonction n'y changera rien) à l'instar de ShowForm d'ailleurs...
Tu pars également du principe que le chemin ne contient pas de "\" terminal, tu le codes en dur. Il serait plus sûr d'utiliser IncludeTrailingPathDelimiter (est-ce une url ?).
Bref, il y a au moins trois problèmes potentiels dont certains peuvent se révéler critiques. Un try..finally ne serait pas du luxe ici.
(Pour le "\", c'est surtout l'utilisateur qui ne va pas comprendre pourquoi ce fichier, présent dans le répertoire, ne veut pas se charger...)
Et la procédure qui va appeler cette procédure devra peut-être également être protégée, TRTEditor.Create pourrait échouer par manque de ressources par exemple. Etc. etc.
try..finally est la seule façon sûr à 100% de désallouer ce qui l'a été. A ne pas négliger.
La mauvaise exécution de LoadFromRT2 provoque visiblement une exception (enfin je l'espère sinon le try...finally n'apporte rien) et transformer cela en fonction qui retourne un code d'erreur est manifestement une incompréhension du principe des exceptions.
L'anticipation des conditions d'erreur revient à faire de la divination car on ne sait pas a priori comment la procédure LoadFromRT2 va évoluer, avec la gestion d'exception, le code reste relativement sain. Si l'on rajoute de nouvelles conditions d'erreurs dans la procédure, il faut théoriquement revoir le code des appelants pour tenir compte de ces nouvelles erreurs potentielles. Se limiter à un retour booléen (True c'est bon, False c'est mauvais) n'est pas très précis.
La complexité intrinsèque de la gestion des codes d'erreur de fonction a motivé l'invention de la gestion par exception qui simplifie grandement le code. Transformer une procédure en fonction pour traiter une condition d'erreur est un palliatif à réserver à des langage sans gestion d'exception comme le langage C ou le Turbo Pascal et alors comment traiter les vraies fonctions dont le résultat provient d'un calcul ? Delphi et Free Pascal évitent cet écueil.
Cdlt
M E N S . A G I T A T . M O L E M
Debian 64bit, Lazarus + FPC -> n'oubliez pas de consulter les FAQ Delphi et Pascal ainsi que les cours et tutoriels Delphi et Pascal
"La théorie, c'est quand on sait tout, mais que rien ne marche. La pratique, c'est quand tout marche, mais qu'on ne sait pas pourquoi. En informatique, la théorie et la pratique sont réunies: rien ne marche et on ne sait pas pourquoi!".
Mais Emmanuel Kant disait aussi : "La théorie sans la pratique est inutile, la pratique sans la théorie est aveugle."
Bof, je le vois comme en C++ : faire des exceptions dans des cas rares - exceptionnels, ou pour faire une adaptation d'une bibliothèque ancienne (généralement écrite en C).
Par exemple, lorsqu'on ne peut pas allouer de la mémoire.
- Rien n’empêche de retourner un type énuméré
- Au lieu de faire un try/ catch qui attrape toutes les exceptions (fourre-tout ), on peut intercepter les exceptions que l'on veut soit 1 par 1 soit par lot. Et on revient au même problème d'évolution qu'avec le retour.
Lorsque je lis que Andnotor soulève 3 problèmes potentiels, mais en définitive ne fait rien à part passer son chemin
Oui bof Lorsque je vois que la VCL me lance une exception lorsqu'on je donne le focus à un UI qui est caché/ sans parent/ ..., cela m'embête profondément.
Parce que le problème des exceptions, c'est qu'on ne connait pas toutes les exceptions lancées par une méthode (à moins que la documentation soit bien faite)
En C++ 03, on peut ajouter à la fin d'une déclaration la liste des exceptions avec le mot clef throw.
Le retour énuméré a ce petit avantage
ne pas confondre try/finally et try/except, le premier permet d'exécuter un code systématiquement, c'est donc tout à fait adapté pour libérer une ressource; le second concerne la gestion des erreurs comme un try/catch
le throw est intéressant mais il a ses limites, mais rien n'empêche de filtrer les erreurs selon le type d'exception
et même de la propager avec raise.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10 try ... except on EZeroDivide do HandleZeroDivide; on EOverflow do HandleOverflow; on EMathError do HandleMathError; else HandleAllOthers; end;
- Try..finally n'est pas un gestionnaire d'exception, juste un bloc inconditionnel ;
- il ne s'agit pas de "faire des exceptions" mais de libérer ce qui a été créé/alloué localement, que la procédure se termine normalement, soit interrompue volontairement ou suite à un problème ;
- je ne suis pas contre transformer LoadFromRT2 en fonction mais ce n'est pas suffisant ;
- à la question "Est-ce que l'on doit aussi utiliser un block try ... finally avec new ?" Ma réponse est oui, par sécurité.
Parce que ce n'est pas le sujet mais si je devais proposer quelque chose, ce serait de garder la structure du code originale, quitte à l'améliorer
Vous ne trouvez pas que la question de Marc_3 manque de contexte ?
D'ailleurs, on ne l'a plus revu dans le coin depuis le NON !
Il a une réponse totalement incomplète et j'espère qu'il suit cet échange !
un Exemple ou le try finally n'est pas possible
Ensuite la libération est effectué par le TThread qui gère la TThreadList, et cette partie ne garanti pas forcément la libération
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12 procedure Bidule.Machin(); var pStruct: PStructure; begin New(pStruct); try pStruct^.Champ := Chose(); ThreadList.Add(pStruct); except Dispose(pStruct); // Chose ou Add/Grow/SetLength peuvent déclencher une exception end; end;
Je l'écrit souvent ainsi ( oui, c'est pas joli joli mais je n'ai pas encore trouvé mieux sans faire une "Usine à gaz" de gestion d'état mais c'est un TOUT AUTRE SUJET)
Vous notez l'absence de finally dans
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 var MoreItem, HaveItem: Boolean; Item: TStruture; // ne contient que des types simples et parfois des string ItemPtr: PStruture; TmpList: TList; begin try if not Terminated then begin MoreItem := False; // Parcours de la liste repeat HaveItem := False; try // Phase 1 - Récupération d'un Elément (on bloque la liste juste le temps qu'il faut) TmpList := FThreadList.LockList(); try if TmpList.Count > 0 then begin ItemPtr := PFile(TmpList.First()); Item := ItemPtr^; // Copie ! TmpList.Delete(0); Dispose(ItemPtr); HaveItem := True; MoreItem := TmpList.Count > 0; end finally FThreadList.UnlockList(); end; except on E: Exception do begin HaveItem := False; LogException(E.ClassType(), E.Message); // Ici cela peut être très poussé, entre log fichier, DB, notification par TCP, par mail ... paranoïa absolue ! end; end; // Phase 2 - Traitement de l'Element ! if HaveItem then begin try ... Item ... except on E: Exception do LogException(E.ClassType(), E.Message); end; end; until not MoreItem or Terminated; end; except on E: Exception do LogException(E.ClassType(), E.Message); end; end;
Après tout, la Copie pourrait échouer pour des type string qui impliquerait une allocation à ce moment (j'avoue que je ne maitrise pas le compteur de référence à ce niveau)
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 ItemPtr := PFile(TmpList.First()); Item := ItemPtr^; // Copie ! TmpList.Delete(0); Dispose(ItemPtr);
[HS]J'ai appris à gérer le EOutOfMemory à l'époque de D4 sur un PC avec 64 Mo RAM où Win98 passait son temps à utiliser le SWAP
Cela t'apprend à coder plus léger
Même Aujourd'hui, à part un logiciel d'imagerie ou d'environnement multi-média (certains gèrent un SWAP interne plus rapide que le SWAP système),
faut programmer comme un sauvage pour saturer la mémoire d'une machine actuelle[/HS]
De même le Delete qui appele la méthode redéfinissable Notify pour déclencher une exception
[HS]J'ai un tas de thread dans ce genre dans module de pilotage de robot ou d'outil supervision ... des programmes qui tournent des semaines échangeant des millions d'évènement entre eux
Je n'ai jamais eu de problème parce que ces modules n'ont que peu de charge en RAM car fonctionne en flux tendu qui n'arrive jamais à un EOutOfMemory
C'est ce sujet qui m'a fait remarquer que j'aurais pu mettre un try finally mais si mon programme provoque une exception à ce niveau du code c'est signe d'une défaillance générale
[/HS]
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 ItemPtr := PFile(TmpList.First()); try Item := ItemPtr^; // Copie ! TmpList.Delete(0); finally Dispose(ItemPtr) end;
Un autre cas ou le try finally semble logique
mais c'est un code bien lourd pour un pointeur locale dont la durée de vie reste interne à la fonction donc on peut écrire simplement
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11 procedure Bidule.Machin(); var pStruct: PStructure; begin New(pStruct); try Chose(pStruct); finally Dispose(pStruct); end; end;
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 procedure Bidule.Machin(); var Struct: TStructure; begin Chose(@Struct); end;
"Doit-on Utiliser try .. finally après un New ?" n'est pas la bonne question !
Pour moi, la bonne question est "A quel moment utliser un New ?"
et même si NON d'ALWEBER me choque par son absence d'argumentation, il n'est pas totalement faux !
le try .. finally pour avoir un code propre qui gère bien la mémoire est une évidence
Mais alloué dynamiquement ce qui n'a pas besoin de l'être complexifie inutilement un code
Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
Attention Troll Méchant !
"Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
L'ignorance n'excuse pas la médiocrité !
L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
Il faut avoir le courage de se tromper et d'apprendre de ses erreurs
Si on envisage un try..finally après New/Create, c'est logiquement que sa portée est locale.
@Shai : MoreItem n'étant pas initialisé, tu risques la boucle sans fin
Si l'on est en portée locale
Pour un objet via Create c'est logique car indispensable puisque de Delphi ne gère que des objets par référence contrairement au C++ où l'on peut faire un objet en allocation statique ou un objet en allocation dynamique
Pour un pointeur géré par GetMem/FreeMem parce que l'on ne peut pas déterminer la taille du buffer en DesignTime le "try .. finally" prend tout son sens
Pour un pointeur géré par New qui si me trompe pas ne fonctionne que sur pointeur typé qui détermine la taille à allouer (sur Pointer cela renvoie nil), c'est donc je pense totalement inutile puisque l'opérateur @ peut faire le travail
[HS]
Chipoteur, tu ne l'as pas vu dans les "..."
c'est ça de copier partiellement un code, bon c'est corrigé par pure cohérence de cet HS
Comme je n'ai AUCUN warning dans ces modules, j'aurais tout de suite vu un "warning W1036: W1036 La variable 'MoreItem' n'a pas été initialisée"
[/HS]
Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
Attention Troll Méchant !
"Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
L'ignorance n'excuse pas la médiocrité !
L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
Il faut avoir le courage de se tromper et d'apprendre de ses erreurs
pas forcément, si tu utilises un Record statique, il est placé sur la Pile, alors que New alloue sur le Tas. Un cas simple ou cela pose problème est celui d'une méthode récursive, avec un Record un peu gros sur la pile tu exploses rapidement la pile, avec un New ça n'arriverais que beaucoup plus tard car tu n'utilises qu'un pointeur dans la pile.
J'allais éditer mon message pour évoquer une structure pointant sur un très gros tableau comme dans ce sujet mais la récursivité est aussi un cas tout à fait intéressant au quel je n'avais pas songé !
Comme quoi, le contexte est bien très important pour bien répondre à la question !
J'espère que Marc_3 va nous lire, car c'est plutôt le OUI qui l'emporte sur le NON
[HS]
tout à fait, j'en ai déjà corrigé dans ce sens en passant MoreItem/HaveItem à False au début du repeat et/ou dans le except
Je vois qu'il m'en reste encore !
[HS]
Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
Attention Troll Méchant !
"Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
L'ignorance n'excuse pas la médiocrité !
L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
Il faut avoir le courage de se tromper et d'apprendre de ses erreurs
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