Liste chaînée d'instances de classe [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
- 4 janv. 2008 à 13:40
Bonjour à tous,

J'ai un petit souci : deux instances de classes sont créées à la même adresse mémoire. Je vous explique mon problème en détail.

Ma première classe contient comme attribut privé FirstXiPtr, de type ^TXi, pointant sur le premier élément d'une liste chaînée d'instances d'une deuxième classe TXi. Chaque instance de TXi, sauf la dernière, contient un pointeur sur l'instance suivante dans la liste. Cet attribut privé de la classe TXi, se nomme NextXiPtr, également de type ^TXi.

Je peine sur la création de cette liste, qui a lieu dans une méthode de la première classe.

La procédure Debug, de la TForm principale affiche simplement un String  dans un TLabel de ma TForm principale. La procédure SetNextXiPtr, de la classe TXi, permet de modifier l'attribut privé NextXiPtr. Je l'ai écrite comme suit.

// le type TXiPtr est définit par TXiPtr = ^TXi;
procedure TXi.SetNextXiPtr(XiPtr: TXiPtr);
begin
  self.NextXiPtr := XiPtr;
end;

Voici comment j'ai écrit la création de la liste, de manière statique dans un premier temps. Les variables locales de ma procédure sont:

TempXi: TXi; // instance temporaire de TXi
CurrentXiPtr: TXiPtr; // pointeur sur l'instance de TXi courante

// création d'une liste de 3 instances de TXi

// on en crée un premier
TempXi := TXi.Create;
AppForm.Debug('un premier TXi est créé à l''adresse '+IntToHex(Byte(@TempXi), 8));
FirstXiPtr := @TempXi;
TempXi := nil;
AppForm.Debug('le premier TXi d''adresse '+IntToHex(Byte(FirstXiPtr), 8)+' est inséré en tête de liste');
// on se place sur l'élément suivant
CurrentXiPtr := FirstXiPtr;
AppForm.Debug('le TXi courant a pour adresse '+IntToHex(Byte(CurrentXiPtr), 8));

// on en crée un deuxième
TempXi := TXi.Create;
AppForm.Debug('un deuxième TXi est créé à l''adresse '+IntToHex(Byte(@TempXi), 8));
CurrentXiPtr.SetNextXiPtr(@TempXi);
AppForm.Debug('le deuxième TXi d''adresse '+IntToHex(Byte(CurrentXiPtr.NextXiPtr), 8)+' complète la liste');
TempXi := nil;
// on se place sur l'élément suivant
CurrentXiPtr := CurrentXiPtr.NextXiPtr; // provoque une erreur EAccessViolation
AppForm.Debug('le TXi courant a pour adresse '+IntToHex(Byte(CurrentXiPtr), 8));

// on en crée un troisième
TempXi := TXi.Create;
AppForm.Debug('un troisième TXi est créé à l''adresse '+IntToHex(Byte(@TempXi), 8));
CurrentXiPtr.SetNextXiPtr(@TempXi);
AppForm.Debug('le troisième TXi d''adresse '+IntToHex(Byte(CurrentXiPtr.NextXiPtr), 8)+' complète la liste');
TempXi := nil;

Je récupère ainsi les traces inscrites sur mon Label par la procédure Debug.

un premier TXi est créé à l'adresse 00000008
le premier TXi d'adresse 00000008 est inséré en tête de liste
le TXi courant a pour adresse 00000008
un deuxième TXi est créé à l'adresse 00000008
le deuxième TXi d'adresse 00000008 complète la liste

Comme le deuxième TXi est créé à la même adresse, je comprends qu'il y ait une erreur à l'execution, mais pourquoi n'est-il pas créé à une adresse mémoire non utilisée ?
Afficher la suite 

8 réponses

Meilleure réponse
Messages postés
1015
Date d'inscription
samedi 25 octobre 2003
Statut
Membre
Dernière intervention
29 août 2013
4
3
Merci
les Objets sont deja des pointeurs.
Quand tu fais IntToHex(Byte(@TempXi), l'adresse qu'il te retourne est l'adresse de ta variable locale 'tempXi', et non de ton objet.
Pour avoir un pointeur sur ton TXi (ce qui n'est pas forcement necessaire etant donné qu TXi est deja un pointeur), il faut que tu fasse :
TempXi := TXi.Create;
new(CurrentXiPtr);
currentXiPtr^ := TempXi;
ca devrait resoudre ton probleme

FirstXiPtr := @TempXi
ne fait que faire pointer firstXiPtr vers l'adresse de ta variable locale tempXi (qui ne sera plus valide a la fin de ta fonction !!!)

Dire « Merci » 3

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

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

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 comprends mieux. Merci beaucoup Guillemouze.

De ce fait, il serait plus correct de déclarer mes pointeurs FirstXiPtr et NextXiPtr de type TXi, et non ^TXi ?
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
tout a fait, il faut conciderer les objets comme des pointeurs, donc a moins d'en avoir l'utilité réelle, il n'est pas necessaire de les manipuler explicitement par des pointeurs.
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
PS: tu peux connaitre leur adresse de pointeur en faisant
IntToHex(integer(TempXi))
les pointeurs sont definis sur 4 octets (comme les integer, mais pas comme les byte (1 octet) ... a moins que tu ne compile en 64bits
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 vois. Connais-tu des cas où manipuler des objets explicitement par des pointeurs est nécessaire, par curiosité ? Après cette question, j'arrète de t'embêter, promis.
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
hmmmmmm pas vraiment, les pointeurs de pointeurs sont des choses assez rares quand meme, surtout en delphi ou la manipulation de pointeurs par les fonctions se fait simplement.
Je m'explique: imagine un fonction qui va modifier une variable exterieure (de type TObject)

procedure ModifieLePointeur(var AObject: TObject);
begin
  AObject := UnAutreObjet;
end;
si tu fais
var
    x: TObject;
begin
    x := UnObjet;
    ModifieLePointeur(x);
    assert(x = UnObjet);
end;

le fait de mettre var va faire que ta variable x a ete modifiée par la fonction

en C par exemple, tu sera obligé de passer par un pointeur de pointeur, ce qui donne (en utilisant la synthaxe delphi):
procedure ModifieLePointeur(ptAObject: ^TObject);

begin

  ptAObject^ : = UnAutreObjet;

end;

dans les 2 cas, si tu n'utilise pas var en delphi ou un pointeur en C, la variable x ne sera pas modifiée par la fonction. Donc delphi te permet de ne pas avoir a utiliser de pointeurs sur des objets.

je vois bien un cas, mais je suis pas sur que ce soit tres propre:
imagine plusieurs objets de type A qui accedent a un objet de type B, qui peut etre detruit par n'importe quel objet A.
Soit $8 l'adresse de B
A1 connait B => A1 a une reference vers l'adresse de B => A1 a un champ valant $8

A2 connait B => A2 a une reference vers l'adresse de B => A2 a un champ valant $8

A1 detruit B => A1 n'a plus de ref vers B => A1 a un champ valant $0 (nil)
l'adresse $8 ne contient plus d'objet B (juste des residus de memoire a pourvoir)
A2 connait toujours B en tant que $8. Si il essaie d'y acceder, il risque l'access violation, ou pire, la valeur incoherente.

par contre, si tu passe par un pointeur intermediaire qui lui ne sera jamais detruit, cad :
soit C le pointeur d'adresse $16
C connait B => C a un pointeur vers $8
A1 et A2 connaissent C => ils ont un pointeur vers $16
A1 detruit B => A1 detruit l'objet B et met la reference de C vers B a nil ($8 -> $0)
C connait B (qui est nil), et A1 et A2 connaissent toujours C qui est toujours en $16
si A2 essaie de regarder B, il va verifier sur qui pointe C et verra que l'objet B pointé par C esr nil.

je sais pas si j'ai été tres clair ...
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
un dessin t'aidera sans dout mieux a comprendre :
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
J'ai compris. Le texte seul ou le schéma seul expliquent bien le principe. Alors avec les deux ensemble, c'est limpide. Merci.

Je vois aussi pourquoi ce n'est pas utilisé tous les jours. C'est vraiment bien comme langage le Delphi.

Maintenant, je n'ai plus qu'à simplifier mon code. Au boulot...

Bon week end Guillemouze, et merci encore.
Commenter la réponse de zwyx