Destruction liste chaînée [Résolu]

Messages postés
152
Date d'inscription
jeudi 22 novembre 2007
Statut
Membre
Dernière intervention
21 mars 2016
- - Dernière réponse : zwyx
Messages postés
152
Date d'inscription
jeudi 22 novembre 2007
Statut
Membre
Dernière intervention
21 mars 2016
- 10 janv. 2008 à 14:59
Bonjour à tous,

J'ai modélisé une classe (TA par exemple) qui contient l'adresse d'une instance d'une autre classe (que l'on nommera TB). Ensuite, chaque instance de la classe TB contient l'adresse de l'instance de TB suivante, de manière à constituer une liste chaînée simple.

Mon souci intervient dans le destructeur de la classe TA, quand je cherche à détruire toutes les instances de TB, en commençant bien évidemment par le dernier, pour ne pas rompre la liste.

Voici un petit schéma ASCII illustrant la modélistation, et le code du destructeur de la classe TA que j'ai écrit.

  ____________
 |   A1: TA   |
 |------------|
 | ...        |        ____________
 | FirstB: TB +------>|   B1: TB   |
 |------------|       |------------|
 | Create();  |       | ...        |        ____________
 | Destroy(); |       | NextB: TB  +------>|   B2: TB   |
 | ...        |       |------------|       |------------|
 |____________|       | Create();  |       | ...        |
                      | Destroy(); |       | NextB: TB  +------> nil
                      | ...        |       |------------|
                      |____________|       | Create();  |
                                           | Destroy(); |
                                           | ...        |
                                           |____________|

destructor TA.Destroy();
var
  CurrentB: TB;
begin
  if FirstB<>nil then // vérification au cas où liste chaînée vide
  begin
    CurrentB := FirstB; // on pointe sur la première instance de la liste
    while CurrentB.NextB<>nil do // tant qu'un élément suit
    begin
      while CurrentB.NextB<>nil do // recherche de la dernière instace de TB constituant la liste chaînée
        CurrentB := CurrentB.NextB;
      CurrentB.Free; // appelle le destructeur de TB pour détruire le dernier élément
    end; // while
    FirstB.Free; // appelle le destructeur de TB pour détruire le premier élément
  end; // if
end;

Lors de l'exécution, le code n' exécute pas le corps de la première boucle while, juste après la ligne
CurrentB := FirstB;

Voila, je vous ai tout dit. Alors si vous avez des indices pour trouver mon erreur...

Merci.
Afficher la suite 

13 réponses

Meilleure réponse
Messages postés
3982
Date d'inscription
mardi 8 mars 2005
Statut
Modérateur
Dernière intervention
7 novembre 2014
9
3
Merci
Salut,


Ce serait pas plus simple si chaque parente supprime sa fille, qui supprime sa fille, qui supprime sa fille...


Free vérifie que le pointeur sur l'objet n'est pas à nil avant d'appeler le vrai destructeur.

destructor TA.Destroy();

begin

    FirstB.Free;
end;

destructor TB.Destroy();

begin

    NextB.Free;
end;

Dire « Merci » 3

Quelques mots de remerciements seront grandement appréciés. Ajouter un commentaire

Codes Sources 119 internautes nous ont dit merci ce mois-ci

Commenter la réponse de cs_rt15
Messages postés
152
Date d'inscription
jeudi 22 novembre 2007
Statut
Membre
Dernière intervention
21 mars 2016
0
Merci
Super bonne idée ! Pourquoi n'y avais-je pas pensé plus tôt.
Comme ça, pas besoin de boucler avec while.
Merci beaucoup pour l'idée.

Par contre, concernant la méthode "Free", elle n'appelait pas le destructeur de ma classe TB, alors je l'ai ré-écrite comme suit:

procedure TB.Free();
begin
  if self<>nil then
    self.Destroy();
end;

Je l'ai également ré-écrite pour ma classe TA. Comme ça, avec ta solution d'appel récursif au destructeur de TB, ça devrait bien tourner.

Merci rt15
Commenter la réponse de zwyx
Messages postés
152
Date d'inscription
jeudi 22 novembre 2007
Statut
Membre
Dernière intervention
21 mars 2016
0
Merci
Ta méthode est efficace. J'ai pu ainsi trouver une erreur lors de la création de ma liste chaînée. C'est sans doute pour cela que ma méthode de destruction initiale ne fonctionnait pas. En effet, j'avais CurrentB.NextB=nil

Merci encore
Commenter la réponse de zwyx
Messages postés
1015
Date d'inscription
samedi 25 octobre 2003
Statut
Membre
Dernière intervention
29 août 2013
4
0
Merci
ce n'est pas normal de reimplementer la méthode Free
as tu vérifier ta declaration du destructeur (dans l'interface) et de ses parents si c'est aussi des classes persos?
destructor Destroy;override;
en tout cas, reimplementer le destructeur ne fera que masquer un problème qui resurgira un jour ou l'autre.
Commenter la réponse de Guillemouze
Messages postés
152
Date d'inscription
jeudi 22 novembre 2007
Statut
Membre
Dernière intervention
21 mars 2016
0
Merci
Je ne vois pas en quoi c'est dangeureux de ré-écrire la méthode Free pour une classe, même si elle est générique, on peut la surcharger, non ?

Enfin je te fais confiance, j'ai effacé mes procédures Free. J'ai ajouté "override" à mes destructeurs de classe. Maintenant, l'appel à Free exécute bien le destructeur que j'ai écrit.

Une dernière question, est-ce bien "override", et non "overload" ? Si je comprend bien la nuance entre ces deux commandes, la première remplace tandis que la deuxième permet de compléter une méthode générique ?

Cordialement,
Commenter la réponse de zwyx
Messages postés
1015
Date d'inscription
samedi 25 octobre 2003
Statut
Membre
Dernière intervention
29 août 2013
4
0
Merci
override permet de surcharger des methodes virtual (ou dynamic).
overload permet d'avoir 2 noms de fonctions identiques, aves des parametres differents.

si tu as une methode statique (non virtual), le pointeur sur cette methode est defini a la compilation.
si tu as une methode virtaul, le pointeur est defini a l'execution

imagine 2 classe :

TPere = class
  procedure Test; virtual ;
end;

TFils = class(Tpere)
  procedure Test; override;
end;
x :TPere.create; //> @(x.Test) = $1x :TFils.Create; // @(x.Test) $2

si tu ne met pas le virtual et le override, l'adresse sera du style
AdresseDeBaseDeLaClasse + AdresseDeLaMethodeDansLaClasse
a ce moment la, c'est la ligne de code qui te permet d'acceder a la methode qui definira laquelle utiliser (en gros, le type de ta variable):

var
    p: TPere;
    f: TFils;
begin
    f : = TFils.Create;
    p := f; // meme objet (de classe TFils)
    p.Test; //appel de la methode de la classe TPere
    f.Test; //appel de la methode de la classe TFils
alors que les 2 variables pointent vers le meme objet !

Pour le overload, tu peux avoir 2 methodes :
procedure Test(x: integer); overload;
procedure Test(x: string); overload;

ici, selon le type du parametre que tu passe a la fonction, l'implementation correspondante sera appelée.
Commenter la réponse de Guillemouze
Messages postés
1015
Date d'inscription
samedi 25 octobre 2003
Statut
Membre
Dernière intervention
29 août 2013
4
0
Merci
Pour en revenir a ton destructeur, voila une petite explication:

Soit  A: TA;

A.Free provoque les appels suivants
TObject.Free
qui est implementé comme ca :
procedure TObject.Free;
begin
  Self.Destroy;
end;

vu que ta methode Destroy n'est pas override, l'adresse de la fonction Destroy trouvée depend du type de la variable self, a savoir TObject.
Donc la methode appelée est TObject.Destroy.

en revanche, si tu met override a ton TA.Destroy, l'adresse trouvée de la methode Destroy dans TObejct.Free est celle de TA, puisqu'elle a ete liée au Create de ton TA.

j'espere avoir ete clair
Commenter la réponse de Guillemouze
Messages postés
152
Date d'inscription
jeudi 22 novembre 2007
Statut
Membre
Dernière intervention
21 mars 2016
0
Merci
Oula, le premier message était compliqué. Mais j'ai quand même saisi la différence entre overload et override. J'ai mieux compris ce qui se passait dans ma situation avec ton deuxième message. La destruction avec Free passe à présent toujours par le destructeur de ma classe, et non plus par celui de TObject.

Encore merci pour ton aide.
Commenter la réponse de zwyx
Messages postés
1015
Date d'inscription
samedi 25 octobre 2003
Statut
Membre
Dernière intervention
29 août 2013
4
0
Merci
"... toujours par le destructeur de ma classe, et non plus par celui de TObject."

il passera par celui de TObject si tu met inherited dans le destructeur de ta classe
Commenter la réponse de Guillemouze
Messages postés
152
Date d'inscription
jeudi 22 novembre 2007
Statut
Membre
Dernière intervention
21 mars 2016
0
Merci
Tu me conseillerais de ne pas mettre inherited si je gère moi même la libération de la mémoire allouée pour les objets dynamiques dans ma classe ?

Qu'en est-il pour le constructeur, inherited n'est peut-être pas non plus nécessaire ?
Commenter la réponse de zwyx
Messages postés
152
Date d'inscription
jeudi 22 novembre 2007
Statut
Membre
Dernière intervention
21 mars 2016
0
Merci
Tu me conseillerais de ne pas mettre inherited si je gère moi même la libération de la mémoire allouée pour les objets dynamiques dans ma classe ?

Qu'en est-il pour le constructeur, inherited n'est peut-être pas non plus nécessaire ?
Commenter la réponse de zwyx
Messages postés
1015
Date d'inscription
samedi 25 octobre 2003
Statut
Membre
Dernière intervention
29 août 2013
4
0
Merci
absolument pas ;)

a moins de le vouloir pour de bonnes raisons, il faut mettre le inherited dans toutes les fonctions override.

exemple :

destructor TA.Destroy; (override de TObject)
begin
  inherited;
  UnChampDeTA.Free;
end;

destructor TB.Destroy; (override de TA)

begin

  inherited;

  UnChampDeTB.Free;

end;

si tu ne met pas le inherited dans TB, l'objet UnChampdeTA ne sera pas detruit.
Le inherited dans le destroy de TA ne sert a rien, vu que TObject.Destroy ne contient aucun code. Cependant, si tu insere une classe intermediare entre TObject et TA ( TA class -> TA class(TC) ), le inherited servira a quelquechose. Donc inutile de le virer, meme si il ne sert a rien.
Commenter la réponse de Guillemouze
Messages postés
152
Date d'inscription
jeudi 22 novembre 2007
Statut
Membre
Dernière intervention
21 mars 2016
0
Merci
Bien compris. J'ai aussi rajouté override à mes construteurs de TA et TB, d'après ce que tu m'as expliqué.

Maintenant, tout est reglé concernant ce sujet.

Merci et Bonne journée
Commenter la réponse de zwyx