Le langage Ada : Types limités et objets contrôlés
Signature : | Mis en ligne le : 13/06/2008
Catégorie(s) :
  • GNU/Linux Magazine
  • | Domaine :
    Commentez

    Retrouvez cet article dans : Linux Magazine 84

    Jusqu’ici nous avons presque toujours déclaré et défini nos enregistrements dans la partie publique des paquetages. Cela présente l’avantage de la simplicité, mais l’inconvénient de permettre à l’utilisateur de faire ce qu’il veut avec le contenu de ces structures. Les déclarations privées, vues dans le sixième article de cette série, ne sont pas toujours suffisantes pour assurer une encapsulation correcte. Imaginons que nous déclarions un type décrivant un polygone, dont le nombre de points peut varier. Nous devrons très certainement faire appel à la mémoire dynamique. Cela pourrait ressembler à ceci : On commence par la déclaration d’un type représentant un point (lignes 2 à 6), suivi d’une fonction facilitant son affichage, puis d’un type tableau de points avec un type pointeur associé. Notre type Polygone n’arrive que ligne 10. Il est déclaré tagged pour en faire un type objet et qualifié private pour que l’utilisateur ne puisse pas accéder directement à son contenu. Sage précaution dès lors que l’on manipule de la mémoire dynamique, n’est-ce pas ? Pourtant cela ne suffit pas. Voyez ce programme d’exemple : Comme vous vous en doutez, la procédure Init() réserve de l’espace pour le tableau de points du polygone, tandis que la procédure Detruire() libère la mémoire précédemment réservée. Compilez et exécutez ce programme : Pas de chance : le programme plante par la levée de l’exception CONSTRAINT_ERROR. Ce qui était en fait parfaitement prévisible. Dans le programme, deux polygones sont déclarés lignes 4 et 5. Le premier est initialisé ligne 7 pour contenir deux points, il est correctement affiché ligne 8. Mais que se passe-t-il ligne 9 ? L’affectation de p1 à p2 effectue une copie membre à membre, c’est-à-dire que le membre pts de p2 prend la valeur du membre éponyme de p1. Or ce membre est un pointeur : on obtient donc deux pointeurs référençant la même zone de mémoire, celle réservée ligne 7. Ici Ada ne fait pas mieux que C/C++ : la destruction demandée ligne 10 libère la mémoire préalablement réservée... mais p1.pts pointe toujours dessus ! Il est donc logique que la demande d’affichage de p1 ligne 11 lève une exception, puisque nous tentons d’accéder à une zone mémoire qui a été libérée. Une telle situation est évidemment intolérable. Prévoir une méthode destinée à effectuer la copie d’un polygone, par exemple une procédure Copier(), n’apporte qu’une solution de façade : rien n’oblige l’utilisateur à utiliser cette procédure, rien de l’empêche d’écrire un code incorrect comme le programme précédent.

    Les types limités

    C’est là qu’interviennent les types limités. Un type enregistrement qualifié par le mot-clef limited interdit explicitement la copie d’une instance dans une autre. Modifions ainsi notre paquetage Polygones : Comme vous pouvez le constater, les modifications sont réellement mineures : simplement l’ajout de limited dans la déclaration et la définition du type Polygone. Mais cela suffit à empêcher notre programme de compiler : la ligne 9, qui effectue une copie entre deux instances de Polygone, est devenue illégale et donc rejetée par le compilateur. Désormais, une procédure dédiée à la copie d’un polygone dans un autre prend véritablement un sens. Notez que le caractère limité d’un type est indépendant de son caractère privé : vous pouvez parfaitement déclarer un type limité tout en laissant libre accès à son contenu. L’exemple suivant est parfaitement légal, sauf naturellement la ligne 10 qui provoquera une erreur de compilation : Toutefois, dans la pratique, ce genre de déclarations présente rarement un grand intérêt.

    Types contrôlés

    Les restrictions imposées par le qualificatif limited sont parfois un peu trop gênantes. Il peut arriver que l’on souhaite autoriser l’affectation entre instances d’un type, sans pour autant abandonner tout contrôle sur l’opération. C’est précisément ce que permettent les types contrôlés, par l’intermédiaire du paquetage Ada.Finalization, ainsi que de maîtriser les séquences de création et de destruction des objets. Il suffit pour cela de faire dériver vos objets du type Controlled, un type objet abstrait déclaré dans Ada.Finalization. Ensuite surdéfinissez les procédures Initialize() (pour la création), Finalize() (pour la destruction) et Adjust() (pour d’éventuelles actions à l’issue d’une copie). Voici ce que devient alors notre paquetage de polygones : Les trois procédures surdéfinies apparaissent lignes 18 à 20, tandis que la ligne 23 réalise la dérivation du type Polygone depuis Controlled. À titre d’exemple, voici les contenus de ces trois procédures : La procédure Initialize() est invoquée lors de la création d’une instance du type Polygone. Ce n’est pas équivalent à la procédure Init() déclarée plus haut, dont le rôle est plutôt d’initialiser cette instance et qui doit être appelée explicitement. Pour faire une analogie, on pourrait dire que Initialize() est l’équivalent d’un constructeur par défaut en C++. Dans notre situation, on se contente d’afficher un message contenant l’adresse de la variable en cours de création. Le type Integer_Address appartient au paquetage System.Storage_Elements, lequel fournit également la fonction To_Integer() permettant de transformer une adresse mémoire en un entier affichable. À l’inverse, la procédure Finalize() est invoquée lors de la destruction d’une instance. Pour poursuivre dans l’analogie, on pourrait l’assimiler à un destructeur en C++. En plus d’un message, nous prenons soin ici de libérer la mémoire qui avait été réservée pour le polygone en question, évitant ainsi la fuite de mémoire qui existait dans le premier programme de démonstration de cet article. Enfin, Adjust() est invoquée juste après qu’une opération de copie ait eu lieu. En réalité, l’affectation d’une instance dans une autre se déroule en trois étapes :
    • 1. L’instance cible de l’affectation (le membre de gauche de l’opérateur :=) est finalisée, sans pour autant être détruite : Finalize() est invoquée.
    • 2. Ensuite les membres sont copiés de la source dans la destination.
    • 3. Puis la procédure Adjust() est invoquée, afin d’ajuster éventuellement le résultat de la copie.
    Dans notre cas, le rôle de Adjust() va être d’effectivement dupliquer le tableau de points (lignes 57-58) en réservant la mémoire nécessaire (ligne 56). Voyons tout cela sur un exemple : Voici ce que cela donne à l’exécution : Vous pouvez constater que la création des instances p1 et p2 a lieu avant le début du programme. L’affectation de p1 dans p2 donne bien lieu à la finalisation, puis à l’ajustement de cette dernière. Enfin les " destructeurs " sont invoqués après la dernière instruction du programme. Signalons pour terminer qu’il existe également dans Ada.Finalization le type Limited_Controlled, offrant les procédures Initialize() et Finalize() pour les types limités. Par contre Adjust() n’est pas disponible, les types limités interdisant les copies entre eux, cette procédure est sans objet.

    Conclusion

    Voilà pour cette présentation des types limités et contrôlés. La prochaine fois, nous aborderons les facilités offertes par Ada pour s’interfacer avec d’autres langages de programmation, notamment les langages C et C++, mais également ce bon vieux Fortran.

    Retrouvez cet article dans : Linux Magazine 84

    Vous souhaitez commenter cet article ?
    Brèves Flux RSS
    Édito : GNU/Linux Magazine 149
    Édito : GNU/Linux Magazine HS N°60
    Édito : Misc 61
    Édito : Linux Pratique 71
    Édito : Linux Essentiel N°25
    Communication RSS Com. RSS Presse
    Lancement de la plateforme de vente en ligne de PDF des Éditions Diamond ! Un...
    Misc N°61 – Communiqué de presse
    GNU/Linux Magazine N°149 – Communiqué de presse
    GNU/Linux Magazine HS N°60 – Communiqué de presse
    Linux Pratique N°71 – Communiqué de presse
    prochainement moteur de recherches des articles
     
    :
    :
    Jours heures minutes secondes
    En kiosque Flux RSS

    Le tout nouveau GNU/Linux Magazine est disponible dès maintenant chez votre marchand de journaux et sur notre site marchand.

    Découvrez le sommaire de ce numéro et un aperçu de ce magazine...

    Lire la suite...

    Le tout nouveau Misc est disponible dès maintenant chez votre marchand de journaux et sur notre site marchand.

    Découvrez le sommaire de ce numéro et un aperçu de ce magazine...

    Lire la suite...

    Le tout nouveau Linux Pratique est disponible dès maintenant chez votre marchand de journaux et sur notre site marchand.

    Découvrez le sommaire de ce numéro et un aperçu de ce magazine...

    Lire la suite...

    Le tout nouveau GNU/Linux Magazine HS est disponible dès maintenant chez votre marchand de journaux et sur notre site marchand.

    Découvrez le sommaire de ce numéro et un aperçu de ce magazine...

    Lire la suite...

    Le tout nouveau Linux Essentiel est disponible dès maintenant chez votre marchand de journaux et sur notre site marchand.

    Découvrez le sommaire de ce numéro et un aperçu de ce magazine...

    Lire la suite...

    Le tout nouveau Misc HS est disponible dès maintenant chez votre marchand de journaux et sur notre site marchand.

    Découvrez le sommaire de ce numéro et un aperçu de ce magazine...

    Lire la suite...