![]() |
|
Marco Cantù
Traduit de l'anglais par Iannis Papageorgiadis [email protected] |
Chapitre 8
|
Ce chapitre doit traiter de la gestion de la mémoire, des diverses zones mémoire, et introduire les tableaux dynamiques. Mais pour l'instant, seule cette dernière partie est disponible.
Delphi 4 introduit une implémentation très simple des tableaux dynamiques, sur le modèle du type dynamique chaîne longue que nous venons de traiter. Comme les chaînes longues, les tableaux dynamiques sont alloués dynamiquement et comportent un compteur de références mais ils n'offrent pas une technique copie-par-écriture (copy-on-write). Ce n'est pas un grand problème puisqu'on peut désallouer un tableau en plaçant sa variable à nil.
Maintenant on peut simplement déclarer un tableau sans spécifier le nombre de composantes et l'allouer avec une taille donnée en utilisant la procédure SetLength. La même procédure est aussi utilisée pour redimensionner un tableau sans perdre son contenu. Il existe également d'autres procédures orientées-chaîne, comme la fonction Copy qu'on peut utiliser avec des tableaux.
Voici un petit extrait de code, qui souligne le fait qu'il faut et déclarer et allouer de la mémoire pour le tableau avant de pouvoir l'utiliser :
procedure TForm1.Button1Click(Sender: TObject); var Array1: array of Integer; begin Array1 [1] := 100; // erreur SetLength (Array1, 100); Array1 [99] := 100; // OK ... end;Comme on indique uniquement le nombre de composantes du tableau, l'indice commence toujours à 0. En Pascal, des tableaux génériques utilisent un indice inférieur différent de zéro et des indices non entiers, deux caractéristiques qui ne sont pas supportées par les tableaux dynamiques. Pour connaître l'état d'un tableau dynamique on peut utiliser les fonction Length, High et Low comme dans tout tableau. Pour les tableaux dynamiques, toutefois, Low retourne toujours 0 et High retourne toujours la longueur moins un. Ceci implique que pour un tableau vide High retourne -1 (valeur bizarre si on y pense, puisqu'elle est inférieure à celle retournée par Low).
Après cette brève introduction, voyons un exemple simple, appelé DynArr et illustré à la figure 8.1. Il est simple en effet, parce que il n'y a rien de très compliqué avec les tableaux dynamiques. Nous l'utiliserons également pour montrer quelques erreurs que les programmeurs pourraient commettre. Le programme déclare deux tableaux globaux et initialise le premier dans le gestionnaire OnCreate :
var Array1, Array2: array of Integer; procedure TForm1.FormCreate(Sender: TObject); begin // allocation SetLength (Array1, 100); end;Ceci place toutes les valeurs à zéro. Ce code d'initialisation permet de commencer à lire et écrire des valeurs du tableau immédiatement, sans aucune crainte d'erreur mémoire. (En admettant bien sûr que l'on n'essaie pas d'accéder à des composantes au-delà de l'indice maximum du tableau). Pour une meilleure initialisation, le programme possède un bouton qui écrit dans chaque cellule du tableau :
procedure TForm1.btnFillClick(Sender: TObject); var I: Integer; begin for I := Low (Array1) to High (Array1) do Array1 [I] := I; end;Le bouton Grandir permet de modifier la taille du tableau sans perdre son contenu. Vous pouvez le tester un utilisant le bouton Saisir valeur après avoir pressé le bouton Grandir :
procedure TForm1.btnGrowClick(Sender: TObject); begin // Agrandir en conservant les valeurs existantes SetLength (Array1, 200); end; procedure TForm1.btnGetClick(Sender: TObject); begin // extraire Caption := IntToStr (Array1 [99]); end;Le seul code un peu complexe se trouve dans l'événement OnClick du bouton Alias. Le programme copie un tableau dans l'autre avec l'opérateur := en créant effectivement un alias (une nouvelle variable faisant référence à la même zone mémoire). A ce point, toutefois, si on modifie un des tableaux, l'autre est également affecté, vu que tous deux font référence à la même zone mémoire :
procedure TForm1.btnAliasClick(Sender: TObject); begin // alias Array2 := Array1; // l'un change (les deux changent) Array2 [99] := 1000; // afficher l'autre Caption := IntToStr (Array1 [99]);La méthode btnAliasClick effectue deux opérations supplémentaires. La première est un test d'égalité des tableaux. Elle ne teste pas les véritables composantes de la structure mais plutôt les zones mémoire auxquelles font référence les tableaux, en vérifiant si les variables sont deux alias du même tableau en mémoire :
procedure TForm1.btnAliasClick(Sender: TObject); begin ... if Array1 = Array2 then Beep; // tronquer le premier tableau Array1 := Copy (Array2, 0, 10); end;La seconde est un appel à la fonction Copy qui non seulement déplace des données d'un tableau à l'autre mais remplace également le premier tableau par un nouveau créé par la fonction. Le résultat est que la variable Array1 fait maintenant référence à un tableau de 11 composantes, si bien que, en pressant le bouton Saisir valeur ou le bouton Placer valeur, se produit une erreur mémoire et se lève une exception (à moins que vous n'ayez décoché la case Vérification des limites, auquel cas l'erreur persiste mais l'exception n'est pas affichée). Le code du bouton Remplir continue à fonctionner convenablement même après cette modification, puisque les composantes à modifier sont déterminées en utilisant les limites actuelles du tableau.
La structure de mémoire décrite dans ce chapitre est typique de la programmation Windows, sujet que nous introduirons au chapitre suivant (sans aller jusqu'à présenter complètement l'utilisation de la VCL).