Utilisation des pointeurs
Les pointeurs, c'est un long sujet tellement omniprésent dans la programmation orientée objet que je ne sais pas par où commencer. On va commencer par les plus simples, ceux qu'on manipule tous les jours sans le savoir: les pointeurs d'objet. En fait, tout ce qui est déclaré dans les sections var et qui "pointe" vers un objet est un pointeur. La simple déclaration :
Exemple de déclaration d'une référence d'objet
var Form1: TForm1;
est un pointeur, car Form1 pointe vers l'espace mémoire occupé par les informations de Form1, et ne la contient pas comme on pourrait le croire au début. L'avantage des pointeurs, c'est que ca permet de désigner un objet d'un type donné et d'accéder à ses propriétés, sans se soucier de l'endroit où il est stocké. Par exemple, si on a plusieurs fiches dans ton projet, on peut déclarer le code suivant :
Utilisation des pointeurs d'objets
var
MaFiche: TForm;
begin
MaFiche := Form1;
{ Ma Fiche pointera vers Form1 }
MaFiche.Caption := 'bla';
{ Caption appartient à la classe de TForm, donc on peut y accéder à l'aide
du pointeur }
MaFiche := Form2;
{ Form1 n'est pas détruit, on change juste l'adresse où MaFiche pointe,
ici vers Form2 }
MaFiche.Caption := 'bla bla';
{ Ici, c'est le caption de Form2 qui sera changé }
end;
En fait les pointeurs permettent d'éxecuter des actions groupées sur des objets de structure commune. Par exemple, si on veut appliquer le même traitement à toutes tes fiches, il suffit de déclarer une fonction qui se chargera de le faire :
Utilité des pointeurs
procedure ChangeFiche(Form: TForm); begin Form.Caption := 'Bla'; Form.Color := clRed; end; ChangeFiche(Form1); ChangeFiche(Form2);
Maintenant, on peut appliquer le même traitement à toutes tes fiches en les passant en paramètre à cette procédure.
Bon, ca c'était les pointeurs d'objet. Maintenant, les autres pointeurs, un tout petit peu plus compliqués mais ça reste abordable. En fait, un pointeur, comme son nom l'indique "pointe" vers une adresse mémoire. Quand on déclare un pointeur, il pointe vers un endroit vide, il est alors égal à nil.
Type pointeur
var P: TPointer; begin P := nil; end;
Ici, le P := nil est inutile puisque P vient juste d'être déclaré. On peut ensuite diriger le pointeur vers l'emplacement que l'on veut, par exemple :
Affectation de pointeur
P := Form1;
mais comme ici, P est déclaré en tant que TPointer et non comme TForm, on ne pourra pas accéder à ses propriétés telles que Caption, Color et autres. On aura juste une référence vers l'adresse de Form1. Pour accéder à ses propriétés, il faudra effectuer un transtypage, c'est à dire considérer le pointeur comme un pointeur de fiche, et ce grâce à l'opérateur as :
Utilisation des pointeurs
var P: TPointer; begin P := Form1; (P as TForm).Caption := 'bla'; end
Cette méthode est possible uniquement grâce au transtypage. Les pointeurs servent aussi à garder une référence vers des objets que l'on créé dynamiquement, sans avoir à déclarer la variable qui gradera son adresse. par exemple :
Utilisation des pointeurs pour les objets
uses Registry; var P: TPointer; begin P := TRegistry.Create; end;
permet de créer un objet TRegistry sans avoir à déclarer un pointeur de type TRegistry. Pour une seule référence, c'est inutile, mais c'est très pratique quand on a des tonnes d'objet de même type à référencer, par exemple, dans un jeu, des missiles.
Disons qu'on déclare des munitions de ce type :
Déclaration classe 'Missile'
type
TMissile = class(TObject);
private
FX, FY: integer;
public
property X: integer read FX write FX;
property Y: integer read FY write FY;
procedure Move;
constructor CreateMissile(X, Y: integer);
end;
constructor TMissile.CreateMissile(X, Y: integer);
begin
inherited Create;
Self.X := X;
Self.Y := Y;
end;
procedure TMissile.Move;
begin
Y := Y + 1;
end;
Maintenant, disons que le vaisseau controllé par le joueur tire. Il faut donc créér un missile. Pour cela, on fait :
Creation d'un objet
TMissile.CreateMissile(Vaisseau.X, Vaisseau.Y);
Mais le problème, c'est que pour faire bouger le missile par la suite, il faut pouvoir appeler sa méthode Move, et il faut pouvoir accéder au missile. Or, si on ne garde pas de référence sur le missile, c'est impossible.
C'est à ce moment que les pointeurs révèlent leur utilité. Il suffit de créér un objet de type TList, qui est une liste de Pointeurs, et d'y ajouter les références.
Ainsi, au démmarage de l'application on déclare :
Objet TList
var
List: TList;
procedure TForm1.FormCreate(Sender: Tobject);
begin
List := TList.Create
{Au passage, List est un pointeur vers un TList, soit un pointeur vers un
pointeur de pointeurs !!! }
end;
Et maintenant, quand on voudra ajouter un missile, on fera simplement :
Ajout à la liste
List.Add(TMissile.CreateMissile(Vaisseau.X, Vaisseau.Y));
et maintenant on pourra accéder aux missiles simplement grâce à la propriété TList.Items :
Récupération élément
var M: TMissile; begin M := List.Items[X]; M.Move; end;
Il existe encore un troisième type de pointeurs, qui sont des pointeurs de structure, très utilisé en C et en Pascal, et en général, dans tous les langages non-objet, bien qu'ils soient aussi utilisés dans la programmation Objet.
Structure MonRecord
type
MonRecord = record
a: integer;
b: integer;
end;
Et bien on peut aussi déclarer des pointeurs de structure, de cette facon :
Pointeur sur structure
type PMonRecord = ^MonRecord;
Ainsi, on peut déclarer une variable pointeur vers un record sans lui alouer la mémoire (elle est à nil au début) :
Déclaration pointeur
var MR: PMonRecord;
On peut ensuite lui allouer de la mémoire, et accéder à ses membres:
Affectation mémoire
var MR: PMonRecord; begin New(MR); MR^.a := 25; end;
Vous remarquez que j'ai ajouté un chapeau entre le MR et son membre. Le chapeau est normalement obligatoire pour un pointeur de structure, bien que Delphi soit assez souple à ce sujet et permette de s'en passer dans la plupart des cas. Vu comme ca, les pointeurs de structure peuvent paraître inutiles, mais si on les imbrique, ca peut devenire très utile.
Imaginons un record et son pointeur déclarés ainsi :
Type TMR
type
PMR = ^TMR;
TMR = record
a: integer;
Next: PMR;
end;
Il y a 2 choses surprenantes dans cette déclaration. Premièrement, on peut déclarer un pointeur de structure avant la déclaration de cette structure. C'est la seule fantaisie du langage Pascal à ce niveau, et qui est très pratique. Ensuite, je déclare un Pointeur de structure à l'intérieur même de cette structure, ce qui est possible grâce au premier point, et dont nous allons voir l'utilité tout de suite.
Imaginons maintenant, toujours en utilisant les mêmes structures, que je déclare le code suivant :
Déclaration PMR
var a: PMR; begin New(a); end;
Le pointeur a pointera donc vers un élément de type TMR. Jusque là, rien de fantastique. Mais à présent déclarons ces 3 fonctions:
Fonctions relatives au PMR
function Add(Node: PMR): PMR;
begin
New(PMR^.Next);
Result := PMR^.Next;
end;
function Count(Node: PMR): integer;
var
Temp: PMR;
i: integer;
begin
i := 1;
Temp := Node^.Next;
while Temp <> nil do
begin
i := i + 1;
Temp := Temp^.Next;
end;
Result := i;
end;
function GetItem(Node: PMR; Index: integer): PMR;
var
Temp: PMR;
i: integer;
begin
Result := nil;
if Index > Count(PMR) then exit;
i := 0;
Temp := Node;
while i < index do
begin
i := i + 1;
Temp := Temp^.Next;
end;
end;
Et nous voici avec la déclaration d'un tableau dynamique de type integer. En ajoutant simplement 2 ou 3 propriétés on a un tableau complet (il manque simplement la suppression d'éléments). En fait, c'est sur ce principe que les tableaux dynamiques de Delphi 4 et Delphi 5 sont basés, même si maintenant leur implémentation les rend beacoup plus simples, mais jusqu'à Delphi 3, On était obligé d'implémenter ce type de chaine, où chaque élément garde un pointeur sur le suivant.
C'est sûr que maintenant ca peut faire rigoler quand on voit que pour déclarer un tableau dynamique avec les dernières versions de Delphi il suffit de faire :
Redimensionnement tableau dynamique
var x: array of integer; begin SetLength(X); end;
3 requête(s) SQL executée(s) en 0.001 Secs - Temps total de génération de la page : 0.007 Secs
