Désolé, je ne te suis pas là !Envoyé par Bruno Orsier
A quoi ça sert la double création de "vList" ?
Désolé, je ne te suis pas là !Envoyé par Bruno Orsier
A quoi ça sert la double création de "vList" ?
Si vous êtes libre, choisissez le Logiciel Libre.
Juste histoire d'en rajouter une couche, vues ce que je considère comme des lacunes dans la syntaxe de Delphi (on en a déjà discuté), je suis du même avis que TicTacToe : toujours rendre "symétrique" la création/libération des objets, et ne jamais, jamais renvoyer un objet comme valeur de retour d'une fonction, ni créer d'objet à l'intérieur d'une procédure, en vue de le transmettre par variable à l'extérieur de ladite procédure.
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 type TMonObjet = class ... end; procedure FaireDesTrucsAMonObjet(MonObjet:TMonObjet;DesTrucs:TTrucs); begin // Pas de retour d'objet, pas de Create ni de Free dans le bloc. MonObjet.LesTrucs:=DesTrucs; end; var UnObjet:TMonObjet; begin UnObjet := TMonObjet.Create; // Create et... FaireDesTrucsAMonObjet(UnObjet,TrucMuche); ... UnObjet.Free; // ... Free dans le même bloc, "symétriques". end;
Et pourquoi c'est permis par Delphi ?Envoyé par CapJack
Cette proposition de Paul Toth est bien correct :
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 function StringListFromFile(const FileName:string):TStringList; begin Result:=TStringList.Create; if FileExist(Path1+FileName) then Result.LoadFromFile(Path1+FileName) else if FileExist(Path2+FileName) then Result.LoadFromFile(Path2+FileName); end; var l1,l2:TStringList; begin l1:=StringListFromFile('file1.txt'); l2:=StringListFromFile('file2.txt'); ... l1.free; l2.free; end;
Si vous êtes libre, choisissez le Logiciel Libre.
le problème est plus profond je penseEnvoyé par sjrd
il relève de la méconnaissance des pointeurs :p
petit rappel donc...
le code ci-dessus fait deux choses
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 var o:TObject; begin o:=TObject.Create; end;
1) il alloue une zone mémoire pour un TObject
2) il place dans la variable "o" l'adresse de cet objet en mémoire
le code ci-dessus fait trois choses
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8 var o1:TObject; o2:TObject; begin o1:=TObject.Create; o2:=o1; end;
1) il alloue une zone mémoire pour un TObject
2) il place dans la variable "o1" l'adresse de cet objet en mémoire
3) il place dans la variable "o2" l'adresse du MEME objet en mémoire
Donc si on en revient au code proposé
le code ci-dessus est en tout point équivalent au précédent
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12 function GetObject:TObject; begin Result:=TObject.Create; end; var o:TObject; begin o:=GetObject; o.Free; end;
1) la fonction GetObject alloue une zone mémoire pour un TObject
2) elle place dans la variable "Result" l'adresse de cet objet en mémoire
3) le code place dans la variable "o" l'adresse du MEME objet en mémoire
"Result" n'est pas ici l'objet lui-même mais seulement son adresse en mémoire, ce n'est donc pas "Result" qui doit être libéré (tout comme on ne libère par un INTEGER par exemple) mais bien l'objet dont l'adresse est contenue dans "Result"...or cet objet sera libéré quand on en aura plus besoin. Pour l'instant on garde son adresse dans la variable "o" et c'est lors de l'appel de "o.Free" qu'il sera libéré.
Un pointer c'est un peu comme un indice dans un tableau, ce tableau n'est rien d'autre que la mémoire
Jolie démo du fameux
"Ce qui ce conçoit bien s'énonce clairement...."
Jai eu qlq fois des soucis avec les "Result := Objet".
Je crois mieux comprendre pourquoi !
Je leurs préférais d'ailleurs des fonctions avec passages de valeurs par adresses.
Dans le cas de cette discussion la propositionest donc tres valable , si ce n'est la meilleure pour garder la logique initiale.Envoyé par TicTacToe
Merci beaucoup Paul TOTH pour votre démonstration, elle est très claire et profiteras j'espère a tous.(dont moi-même bien-sûr )
Si vous êtes libre, choisissez le Logiciel Libre.
Ne mélangeons pas syntaxe du langage et bonnes habitudes de développement.Envoyé par TryExceptEnd
Personnellement, tout ce qui a été développé dans ce fil m'est acquis depuis au moins quinze ans. Le problème, c'est que renvoyer un objet créé à l'intérieur d'une fonction comme valeur de cette fonction me hérisse le poil, d'un point de vue logique et théorique.
C'est parfaitement possible, oui, mais horriblement dangereux. Reparlons-en quand vous aurez fait l'expérience des fameuse "fuites de mémoire" prétendument inexplicables...
tu peux développer ?Envoyé par CapJack
ça n'a pas grand chose à voir, voici un exemple de fuite de mémoire sans fonctionEnvoyé par CapJack
ça c'est la pour la question de logique
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 procedure test; var t:TObject; begin t:=TObject.Create; end;
et un exemple d'usage de fonction sans fuite
ce qui s'apparente à la définition d'un singleton ...ça c'est pour la théorie
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 unit test; interface function getInstance:TObject; implementation var gInstance:TObject; function getInstance:TObject; begin if gInstance=nil then gInstance:=TObject.Create; Result:=gInstance; end; initialization gInstance:=nil; finalization gInstance.Free; end.
en plus je vois pleins d'autres cas ou l'usage d'une fonction est idéal, comme par exemple :
en fait tout est possible, il suffit de savoir ce qu'on fait
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8 function TMyParent.AddChild:TCustomChild; begin if assigned(fOnGetChild) then Result:=fOnGetChild(Self) else Result:=TDefaultChild.Create(Self); end;
Non, je n'ai pas envie de développer ni de polémiquer sur le sujet. Il y a déjà eu d'autres problèmes du même genre soulevés, et j'ai déjà expliqué mon point de vue sur la question plusieurs fois. Comme tu dis, "il suffit de savoir ce qu'on fait", mais l'observation de bien des codes et de bien des habitudes montre que c'est loin d'être le cas de tout le monde. Je le dis sans aucune forfanterie.
Ton dernier exemple n'est pas convaincant car il s'agit de la création d'un composant associé à un composant parent. Dans ce cas, le composant créé est ajouté à une liste, et sera donc automatiquement détruit à la destruction du parent. On est dans un cas très particulier, dans lequel un mécanisme spécifique est en jeu.
Quant à l'exemple précédent, l'utilisation d'une variable globale... hum hum. C'est vraiment vouloir prouver qu'on a raison par tous les moyens, y compris les plus malhonnêtes.
Edit : Précisons : je n'ai évidemment jamais prétendu qu'il y avait bijection entre les fuites de mémoire et l'utilisation des fonctions, je dis simplement que la création d'un objet dans une fonction, objet qu'on oublie ensuite de libérer, est une erreur fréquemment commise par les débutants en Delphi, et que dans un souci de pédagogie, je propose d'éviter aux débutants des raccourcis qui ressemblent fort à des pièges qu'ils se tendent eux-même. Maintenant on a le droit de ne pas être d'accord avec ce point de vue.
dommage, j'ai raté tes explications, j'aurais bien aimé savoir ce qui te "hérisse le poil"Envoyé par CapJack
serais-tu de ceux qui considèrent qu'il faut programmer sous Delphi comme si les variables globales et les fonctions n'existaient pas pour ne faire que du 100% objet ?Envoyé par CapJack
Note que la variable de mon exemple n'est pas réellement "globale" puisqu'elle est locale à l'unité.
Pour moi, la pédagogie n'a jamais consisté à prendre les gens pour des cons en leur faisant croire qu'il ne faut pas faire des choses dont on pense qu'ils ne les maitriseront pas...mais bien d'énoncer clairement ce dont il retourneEnvoyé par CapJack
Tu trouves mes exemples non pertinents ou barbares, mais le débutant ne fera pas les mêmes distinctions si on lui bourre le crane en disant qu'une fonction ne doit pas retourner un objet qu'elle instancie car c'est simplement faux.
Et je ne suis pas du genre à vouloir embrouiller les gens...j'ai d'ailleurs regardé de travers l'arrivée des tableaux dynamique qui noircissent des pages et des pages de forums sur pourquoi mon code déconne...tout comme la compatibilité chaine "longue" et PChar
A bas l'obscurantisme !
Salut a tous
Voici une fonction qui peut t'aider
le showmessage( result.Text) c'est juste pour voir le resultat
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 function TForm1.pModeReglement:TStringList; var pModeReglement1:TStringList; begin pModeReglement1:=TStringList.Create; try with pModeReglement1 do begin Add(ComboBox1.Text); Add(edit1.text); end; result:=pModeReglement1; showmessage( result.Text) finally pModeReglement1.free; end;
Je n'affirme pas qu'elle ne doit pas, mais qu'il vaut mieux éviter tant qu'on ne maîtrise pas la programmation objet... je suis d'accord avec toi sur le plan de la programmation, mais :Envoyé par Paul TOTH
Je suis convaincu que dans tout apprentissage il faut y aller petit à petit, et que ce n'est pas "prendre les gens pour des cons" que d'essayer de les faire progresser étape par étape : chaque chose en son temps. Prendre les gens pour des cons, ce serait selon moi aider de façon cyniquement incompréhensible. Ce n'est pas pour rien si dans les programmes officiels de l'école, il y a une progression à respecter strictement. Mais c'est un vaste débat...
Pour en revenir à notre exemple : faire renvoyer par une fonction un objet qu'elle instancie, et gérer correctement en retour la libération de cet objet, sans parler de la gestion des exceptions, n'est - selon moi, mais je me trompe certainement - pas une compétence de débutant, c'est tout. Entre autres, à cause du déréférencement automatique des pointeurs, par exemple.
Edit : mamou30, tu illustres parfaitement mon propos : tu libères systématiquement l'objet qui va être renvoyé par la fonction, ce qui fait que le résultat renvoyé ne sera jamais valide !
je suis tombé sur un autre post de toi sur ce que tu qualifies de problème : le "déréférencement automatique des pointeurs".Envoyé par CapJack
Pour avoir longtemps travaillé en Turbo Pascal avec les "OBJECT" qui sont les objets statiques que tu aurais aimé avoir, je peux te dire que j'approuve totalement le choix qui a été fait par Borland de faire des classes Delphi de pointeurs automatiquement
99% de mes sources Turbo Pascal de l'époque utilisaient des ^Object avec des MonObject^.Propriété...c'est presque aussi chiant que le "->" des autres langages.
Avoir rendu le "^" optionnel sur les pointeurs et illégal sur les "CLASS" est une bonne chose à mes yeux
Alors c'est sur que quand tu viens d'un autre langage ou tout est objet, tu as tendance à vouloir faire du 100% objet en Pascal, et je pense personnellement que c'est une erreur !
Si tu veux du 100% objet, tu as des langages qui l'impose, autant les utiliser. Si tu le fais en Pascal, grand bien t'en face, mais prétendre que c'est comme ça qui faut programmer en Pascal est une hérésie à mes yeux
La simple notion d'Unité qui est, me semble-t-il propre à ce langage, permet de gérer automatiquement les namespaces et des variables "globales mais privées" comme mon gInstance de toute à l'heure. Ne pas utiliser ces possibilités du langage c'est se priver d'outils bien pratiques. Tout comme je préfère de loin une unité SysUtils ou DateUtils avec toutes ces fonctions, qu'une classe TDate avec des fonctions de classe qui ne fait qu'ajouter une couche là ou l'unité répond parfaitement au besoin.
Enfin bon, ce débat date pas d'hier, et ne se terminera pas demain
Euh... Ce n'est pas moi qui ai inventé les termes de déréférencer ou déréférencement, mais Borland itself dans la seule doc version papier que j'ai, celle de Delphi 3. D'ailleurs cette doc utilise systématiquement le terme de référence d'objet, pour parler du contenu d'une variable de type classe.Envoyé par Paul TOTH
Ben justement ! C'est bien parce que nous avons tous les deux, à une époque donnée, connu cette syntaxe, que nous maîtrisons parfaitement le sujet, et que nous savons ce que recouvre la syntaxe Objet.Truc. Encore une fois, je suis désolé d'insister lourdement, mais c'est loin d'être acquis pour quelqu'un qui commence à travailler en Delphi. C'est tout ce que j'ai dit : d'ailleurs le fil auquel tu fais référence, si mes souvenirs sont bons, avait été initié par quelqu'un qui avait récupéré un vieux code source manifestement de l'époque pré-Delphi (ça fait un peu archéologue... ), nécessitant une adaptation.Envoyé par Paul TOTH
Euh... je n'ai rien affirmé de tout ça. J'ai juste dit qu'il me semblait dangereux pour quelqu'un qui débute, de faire renvoyer par une fonction un objet que la fonction instancie, j'ai un peu précisé pourquoi, et c'est tout !Envoyé par Paul TOTH
Ben, euh... oui, oui...Envoyé par Paul TOTH
Il y avait un smiley pour exprimer l'ironie du terme "mauvaise foi", hein ?
oups, désolé, j'y suis aller un peu fort et j'ai présumé de tes idées
et les débutants doivent avoir décroché de notre débat
bonne soirée
Ya pas d'mal...
En fait, c'est juste une question de style de programmation. Je me suis peut-être mal exprimé au début, en voulant aller trop vite. Je vais conclure en essayant de clarifier au maximum mon idée.
Une règle d'Or à laquelle je me soumets moi-même, depuis bientôt un quart de siècle que je programme en Pascal, et bien qu'elle ne soit en aucune manière une obligation liée à la syntaxe du langage, est la suivante :
toujours laisser l'affectation d'un pointeur à la charge de la procedure appelante..
En d'autres termes, j'évite ceci :
Je préfère celà :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12 procedure Appelée(var P:PMonTruc); begin New(p); // ... remplir p^ avec des machins end; procedure Appelante; var p:PMonTruc; begin Appelée(p); Dispose(p); end;
Je trouve en effet plus élégant de coder l'affectation et la libération dans le même bloc de code, plutôt que l'affectation à un endroit, et la libération à un autre. En plus, l'expérience personnelle m'a prouvé que de s'imposer ce genre de contrainte rend le code plus robuste et plus facile à maintenir et à comprendre sur le long terme. On évite plus facilement l'oubli de libérer la mémoire utilisée, car le code est visuellement symétrique. En tout cas, pour moi, c'est presque une nécessité intellectuelle.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12 procedure Appelée(const P:PMonTruc); begin // ... remplir p^ avec des machins end; procedure Appelante; var p:PMonTruc; begin New(p); Appelée(p); Dispose(p); end;
Ensuite, qu'on renvoie le pointeur affecté dans un paramètre var ou dans le Result d'une fonction revient fondamentalement au même du point de vue de cette règle que je m'impose : c'est toujours le probèlme de l'affectation à un endroit, libération à un autre.
Enfin, s'agissant des objets, on ne le dira jamais assez, les variables objet ne sont en Delphi que des références, donc des pointeurs. Donc, j'applique la règle précédente, avec la contrainte renforcée qu'en plus d'affecter/libérer un pointeur, on instancie/libère un objet.
En espérant avoir cette fois été plus clair.
tant mieuxEnvoyé par CapJack
oui, je comprend bien la démarche. Mais elle va à l'encontre d'une autre règle que j'affectionne personnellement réduire le code au minimum, ce qui permet de le mieux comprendre.Envoyé par CapJack
Ainsi, une fonction qui attend un paramètre qu'on va devoir instancier à chaque fois qu'on l'appelle, va m'obliger à une redondance de code qui me hérisse le poil sur le coup Quitte à faire deux procédures
du coup j'ai le meilleur des deux mondes, et j'évite une ligne par procédure (le create du TStringList )
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12 // exemple bidon :D procedure LireFichier(AList:TStrings; const AFileName:string): begin AList.LoadFormFile(AFileName); end; function ListeFichier(cosnt AFileName:string):TStrings; begin Result:=TStringList.Create; LireFichier(Result,AFileName); end;
maintenant, en effet tout cela est sujet à fuite de mémoire...C'est pourquoi j'ai pris la sale habitude d'utiliser des STRING quand c'est possible
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 // attention, je parle ici par exemple du traitement d'un fichier binaire // on d'un flux IP // pas question de faire ce genre de gymnastique dans un contexte normal :) function GetData:string; var MaStructure:^TMaStructure; begin SetLength(Result,SizeOf(TMaStructure)); MaStructure:=@Result[1]; MaStructure.Field1=... end; procedure ProcessData(const Str:string); var MaStructure:^TMaStructure; begin assert(Lengh(Str)=SizeOf(TMaStructure)); MaStructure:=@Str[1]; if MaStructure.Field1=... end;
D'aprés ce que j'ai compris, j'ai deux solutions a mon probleme que je résume comme suit :
- la 1ere utilise une fonction :
- la 2eme utilise une procedure :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13 procedure Quelconque; var vList:TStringList; begin vList:=FModeReglement; vList.Free;//Liberation de l'objet end; function FModeReglement:TStringList; begin Result:=TStringList.Create;//Creation de l'objet Result.Add('Fonction'); Result.Add('Text'); end;
Ai-je bien résumé le pourquoi du comment de ce thread ?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13 procedure Quelconque; var vList:TStringList; begin vList:=TStringList.Create;//Creation de l'objet PModeReglement(vList); vList.Free;//Liberation de l'objet end; procedure PModeReglement(sList:TStringList); begin sList.Add('Procedure'); sList.Add('Test'); end;
Si vous êtes libre, choisissez le Logiciel Libre.
Envoyé par TryExceptEnd
Meme nombres de lignes, mais 2 méthodes différentes
--> Yapluka
Et n'oublie pas le 'résolu' si c'est bon pour toi
Section Delphi
La mine d'or: La FAQ, les Sources
Un développement compliqué paraitra simple pour l'utilisateur, frustrant non ?
Notre revanche ? l'inverse est aussi vrai ;-)
même nombre de lignes quand on ne fait qu'un appel à la fonction...mais dans ce cas je ne vois pas l'intérêt de faire une fonction, quelque soit la méthodeEnvoyé par TicTacToe
encore que s'il y a du code d'initialisation supplémentaire, ça peut être intéressant pour rendre le code plus lisible
on peut aussi déclarer ceci...mais bon
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 procedure demo; var font:TFont; begin font:=CreateFont('Terminal',12); ... font.Free; end; function CreateFont(const FontName:string; Size:integer):TFont; begin Result:=TFont.Create; Result.FontName:=FontName; Result.Size:=Size; end;
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 type TMyFont=class(TFont) public constructor Create(const FontName:string; Size:integer); reintroduce; end; ...
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