zwyx
Messages postés146Date d'inscriptionjeudi 22 novembre 2007StatutMembreDernière intervention21 mars 2016
-
4 janv. 2008 à 09:58
zwyx
Messages postés146Date d'inscriptionjeudi 22 novembre 2007StatutMembreDernière intervention21 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 ?
Guillemouze
Messages postés991Date d'inscriptionsamedi 25 octobre 2003StatutMembreDernière intervention29 août 20136 4 janv. 2008 à 11:26
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 !!!)
Guillemouze
Messages postés991Date d'inscriptionsamedi 25 octobre 2003StatutMembreDernière intervention29 août 20136 4 janv. 2008 à 11:44
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.
Guillemouze
Messages postés991Date d'inscriptionsamedi 25 octobre 2003StatutMembreDernière intervention29 août 20136 4 janv. 2008 à 11:46
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
Vous n’avez pas trouvé la réponse que vous recherchez ?
zwyx
Messages postés146Date d'inscriptionjeudi 22 novembre 2007StatutMembreDernière intervention21 mars 2016 4 janv. 2008 à 11:49
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.
Guillemouze
Messages postés991Date d'inscriptionsamedi 25 octobre 2003StatutMembreDernière intervention29 août 20136 4 janv. 2008 à 12:48
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.