Comportement normal ?

Résolu
Guillemouze Messages postés 991 Date d'inscription samedi 25 octobre 2003 Statut Membre Dernière intervention 29 août 2013 - 25 sept. 2007 à 18:29
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 - 26 sept. 2007 à 18:40
salut a tous, j'ai un petit probleme, et je me demande si ce comportement est normal.
En gros, delphi me reaffecte toujours la meme adresse memoire; ce qui me pose probleme car je fais une comparaison de pointeur qui me dit que le pointeur est le meme alors que l'objet a ete recréé.
Voici grosso modo ma structure

2 classes : TCmd et TOwner, TCmd contient un pointeur vers un TOwner.
une instance cmd: TCmd pointant vers une instance owner:TOwner.
une liste:TObjectList contenue dans owner

Les appels sont les suivants :

cmd := owner.liste.last;
cmd.back;
    -> cmd.Terminer;
       ->owner.Terminer(cmd);
          ->liste.Remove(cmd); //liste.ownsobjects = true
            liste.Add(TCmd.create); // l'objet cree a la meme adresse que "cmd" !!!
          <-
       <-
    <-
cmd2 := owner.liste.last;if cmd2 cmd then onRecommenceLeTraitement; // cmd2 cmd alors qu'il a bien ete detruit

Pensez vous que ce comportement est normal? En effet, je recréé un objet d'un type juste apres la liberation d'un objet du meme type.
Si c'est normal, avez-vous une solution? Creer un objet bidon entre temps pour decaler la memoire?

Merci

6 réponses

cs_Loda Messages postés 814 Date d'inscription vendredi 3 novembre 2000 Statut Membre Dernière intervention 30 juillet 2009 3
26 sept. 2007 à 13:12
re,

le but de cela
"if cmd2 = cmd then onRecommenceLeTraitement;"
est bien de savoir si il s'agit du même object? j'ai juste?
donc un ID devrait faire le truc. non?

mais, de manière général. Une instance qui a été libérée devrait toujours être affectée à NIL. Sauf si tu es sur que tu n'y fait plus référence plus tard. Et tu es exactement dans le cas contraire: tu sais que tu y fait référence plus tard.

la méthode de comparaison d'adresse pour des objects est bien et très utile, lorsque tu compare des objects qui n'ont pas été libéré. sinon, ça vaut rien: c'est pas fiable et dangereux (AV).

bref, si ton code est conséquent, j'ajouterais un ID. Le plus simple et fiable.

genre:
implementation:
var
  lastID :integer = 0;

contructor TBaseMaClass.Create();
begin
  fID := lastID;
  inc(lastID);
 ....
end;

bon code,
Loda
<hr size="2" width="100%" />Se poser les bonnes questions est le premier pas pour trouver les bonnes réponses.
3
cs_Loda Messages postés 814 Date d'inscription vendredi 3 novembre 2000 Statut Membre Dernière intervention 30 juillet 2009 3
26 sept. 2007 à 10:27
salut,
tu ne devrait pas tester des ref d'object qui on été detruit!

pourquoi ne code tu pas une méthode "clear" ou ajout un flag "done". Cela d'évetra aussi le free suivit de l'alloc (qui n'est pas le plus rapide)

Aussi un autre solution, consiste à utilise un ID d'instance. (compteur d'instance) et comparer les ID.

sinon, affecte nil à cmd lorsque tu le detruit.

bon code,

Loda
<hr size="2" width="100%" />Se poser les bonnes questions est le premier pas pour trouver les bonnes réponses.
0
Guillemouze Messages postés 991 Date d'inscription samedi 25 octobre 2003 Statut Membre Dernière intervention 29 août 2013 6
26 sept. 2007 à 12:35
c'est vrai que c'est pas terrible de tester une ref d'objet qui a ete detruit.
mais en fait, le traitement effectué derriere tout ca est un peu plus compliqué .
Par exemple, cmd.back ne fait appel a cmd.Terminer que dans un certain cas, sinon un autre traitement est effectué qui ne provoque pas la destruction de cmd. Le liste.Remove ... liste.Add n'est pas non plus benin, je traite de classes heritees de TCmd, et je ne fais pas toujours le add.
Les appels que j'ai decrit sont le process qui me pose probleme, mais c'est loin d'etre le seul chemin, et je n'ai decris que les actions importantes pour comprendre le probleme.

je pense que le probleme vient surtout de la conception de ma structure generale
Au passage, quelque chose qui peut parraitre choquant est le fait que la fonction TCmd.Terminer par exemple contient une partie executée sur une instance detruite !!!
procedure TCmd.Terminer;
begin
    ...
    Owner.Terminer(Self);
    ... //ce code est executé alors que self est detruit ! (il n'y a en fait aucun code ici)
end;

PS: j'ai fait un petit test :
var
  s1, s2: TStringList;
begin
  s1 := TStringList.Create;
  s1.Free;
  s2 := TStringList.Create;
  ShowMessage(BoolToStr(s1 = s2, true)); //ca affiche bien "true"
end;
0
Guillemouze Messages postés 991 Date d'inscription samedi 25 octobre 2003 Statut Membre Dernière intervention 29 août 2013 6
26 sept. 2007 à 13:26
lol, c'est exactement ce que j'ai fait en attendant de trouver une autre solution, si elle existe

tu as bien compris le but de la manoeuvre.
tu dis : "Une instance qui a été libérée devrait toujours être affectée à NIL". c'est bien gentil, mais affecter nil a une variable ne veut pas dire que toutes les references vers cet objet seront nil. en l'occurence, mon objet se trouve dans une TObjectList (la reference de base). ma cmd n'est qu'une variable temporaire, dans une procedure quelconque, pointant vers celui ci. Je ne peux donc pas, au moment de la liberation de mon objet (=sa suppression de la TObjectList), mettre cette varable a nil, qui est totalement inconnue du possesseur de l'objet !!!
ceci dit, ta phrase reste vrai tout de meme, on ne free pas une variable "globale" sans la niler
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
cs_Loda Messages postés 814 Date d'inscription vendredi 3 novembre 2000 Statut Membre Dernière intervention 30 juillet 2009 3
26 sept. 2007 à 14:07
"C'est bien gentil, mais affecter nil a une variable ne veut pas dire que toutes les references vers cet objet seront nil."

C'est vrai ! et C'est là que les AV arrivent. Les réf sur des object qui risque d'être détruit à tout moment: à éviter.

Note qu'une gestion par ID permet d'éviter quelques peu ce type de problème. Si tu garde l'ID et non la réf de l'objet, lorsque tu vas récupere l'object depuis un autre endroit, tu verra que l'ID ne point plus sur rien. Mais c'est plus lent !

Note aussi, que tu peux garder une list des instance crées (dans TOUT ton programme) dans l'unit de la class.

var
  MaListPriveeGlobal : TListObject; //own = false

contructor TBaseMaClass.Create();
begin
...
  MaListPriveeGlobal.add(self);
...

et dans le destroy tu l'enlève.

ceci te permet d'ajout une méthode global (ou dit de class) qui te retourne une instance en fct de son ID.

bon code!

Loda

PS:
" ta phrase reste vrai" , c'est pas de moi. C'est une habitude de prog. ;-)
Le point important est que le test if assigned(ref) then ref.Do ne marche QUE si tu assign NIL a ta ref après l'avoir detruite!
et que le test assigned est le seul moyen d'empecher des AV (a part un try except vide. si on peu appelé ça empêcher)

ceci devient important si tu as des constructeur et destructeur un peu complexe ou avec des options
<hr size ="2" width="100%" />Se poser les bonnes questions est le premier pas pour trouver les bonnes réponses.
0
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
26 sept. 2007 à 18:40
un programme a toujours un certain espace memoire.
donc les pointeurs qui vont etre creer, auront une adresse memoire dans un interval donné.

exemple :

procedure DisposeAndNil(var ptr: pointer);
begin
  dispose(ptr);
  ptr := nil;
end;

function GetAddressStr(const ptr : pointer) : string;
begin
  if ptr = nil then
    result := 'null'
  else
    result := format('0x%.8x',[longint( ptr )]);
end;

var
  A,B,C,D : ^byte;

procedure TForm1.FormCreate(Sender: TObject);
begin
  ListBox1.Items.Add('cree A');
  New(A);
  A^ := 0;
  ListBox1.Items.Add('A = '+GetAddressStr(A));

  ListBox1.Items.Add('cree B');
  New(B);
  B^ := 0;
  ListBox1.Items.Add('B = '+GetAddressStr(B));

  ListBox1.Items.Add('Libere A');
  DisposeAndNil(pointer(A));
  ListBox1.Items.Add('A = '+GetAddressStr(A));

  ListBox1.Items.Add('Cree C');
  New(C);
  C^ := 0;
  ListBox1.Items.Add('C = '+GetAddressStr(C));

  ListBox1.Items.Add('Cree D');
  New(D);
  D^ := 0;
  ListBox1.Items.Add('D = '+GetAddressStr(D));

  ListBox1.Items.Add('Libere C');
  DisposeAndNil(pointer(C));
  ListBox1.Items.Add('C = '+GetAddressStr(C));

  ListBox1.Items.Add('Libere B');
  DisposeAndNil(pointer(B));
  ListBox1.Items.Add('B = '+GetAddressStr(B));

  ListBox1.Items.Add('Libere D');
  DisposeAndNil(pointer(D));
  ListBox1.Items.Add('D = '+GetAddressStr(D));
end;

vus qu'une addresse se libere (celle de A) avant la creation de C, C reprend l'addresse libre (donc celle de A)
si on recrée A a la fin (quand B,C,D sont libre), A reprendra l'addresse memoire qu'il avait au tout debut puisque libre.
Mais attention, ce fonctionnement est propre a Windows, il n'est pas certains que Linux, Unix ou MacOS gere les pointeurs de la même façon.

<hr size="2" width="100%" />
http://deefaze.gnomz.com
0
Rejoignez-nous