Section courante

A propos

Section administrative du site

Les types

Lorsque vous déclarez une variable, vous devez indiquer son type. Le type d'une variable circonscrit l'ensemble des valeurs qu'elle peut avoir et les opérations pouvant être effectuées sur elle. Une déclaration de type spécifie l'identificateur désignant un type.

Lorsqu'un identificateur apparaît sur le côté gauche d'une déclaration de type, il est déclaré comme identificateur de type pour le bloc dans lequel la déclaration de type se produit. La portée d'un identificateur de type ne s'inclut pas, sauf pour les types de pointeur.

Il existe 5 grandes classes de types.

Types simples

Les types simples définissent des ensembles ordonnés de valeurs.

Un identificateur de type réel est l'un des identificateurs standard suivant : Real, Single, Double, Extended ou Comp.

Ordinal types

Les types ordinaux sont un sous-ensemble de types simples. Tous les types simples autres que les types réels sont des types ordinaux, étant déclenchés par 6 caractéristiques :

La syntaxe d'un type ordinal est la suivante :

Le Turbo Pascal a 10 types ordinaux prédéfinis : Integer, ShortInt, LongInt, Byte, Word, Boolean, ByteBool, WordBool, LongBool et Char. De plus, il existe deux autres classes de types ordinaux définis par l'utilisateur : les types énumérés et les types d'intervalles.

Types entier

Il existe 5 types d'entiers prédéfinis : ShortInt, Integer, LongInt, Byte et Word. Chaque type désigne un sous-ensemble spécifique des nombres entiers, selon le tableau suivant :

Type Intervalle Format
Shortint -128..127 8 bits signé
Integer -32768..32767 16 bits signé
LongInt -2147483648..2147483647 32 bits signé
Byte 0..255 8 bits non-signé
Word 0..65535 16 bits non-signé

Les opérations arithmétiques avec des opérandes de type entier utilisent une précision de 8 bits, 16 bits ou 32 bits, selon les règles suivantes :

Une valeur de type entier peut être explicitement convertie en un autre type entier par transtypage.

Types booléens

Il existe quatre types booléens prédéfinis : Boolean, ByteBool, WordBool et LongBool. Les valeurs booléennes sont désignées par les identificateurs constants prédéfinis False et True. Étant donné que les booléens sont des types énumérés, ces relations sont valables :

Les variables Boolean et ByteBool occupent un octet, une variable WordBool occupe deux octets (un mot) et une variable LongBool occupe quatre octets (deux mots). Le Boolean est le type préféré et utilise le moins de mémoire ; ByteBool, WordBool et LongBool existent principalement pour assurer la compatibilité avec d'autres langages de programmation et l'environnement Windows.

Une variable booléenne ne peut prendre que les valeurs ordinales et 1, mais les variables de type ByteBool, WordBool et LongBool peuvent prendre d'autres valeurs ordinales. Une expression de type ByteBool, WordBool ou LongBool est considérée comme False lorsque sa valeur ordinale est zéro et True lorsque sa valeur ordinale est différente de zéro. Chaque fois qu'une valeur ByteBool, WordBool ou LongBool est utilisée dans un contexte où une valeur booléenne est attendue, le compilateur génère automatiquement du code convertissant toute valeur différente de zéro en la valeur True.

Type de caractère

L'ensemble de valeurs de Char sont des caractères, classés selon l'ensemble de caractères ASCII étendu. L'appel de fonction Ord(Ch), où Ch est une valeur Char, renvoie l'ordinalité de Ch.

Une constante de chaîne de caractères de longueur 1 peut désigner une valeur de caractère constante. Toute valeur de caractère peut être générée avec la fonction standard Chr.

Types énumérés

Les types énumérés définissent des ensembles ordonnés de valeurs en énumérant les identificateurs dénotant ces valeurs. Leur ordre suit l'ordre dans lequel les identificateurs sont énumérés.

Lorsqu'un identificateur apparaît dans la liste des identificateurs d'un type énuméré, il est déclaré en tant que constante pour le bloc dans lequel le type énuméré est déclaré. Le type de cette constante est le type énuméré déclaré.

L'ordinalité d'une constante énumérée est déterminée par sa position dans la liste d'identificateurs dans laquelle elle est déclarée. Le type énuméré dans lequel elle est déclarée devient le type de la constante. La première constante énumérée dans une liste a une ordinalité de zéro.

Voici un exemple de type énuméré :

  1. Type
  2.  Suit = (Club, Diamond, Heart, Spade);

Compte tenu de ces déclarations, Diamond est une constante de type Suit. Lorsque la fonction Ord est appliquée à la valeur d'un type énuméré, Ord renvoie un entier indiquant où se situe la valeur par rapport aux autres valeurs du type énuméré. Compte tenu des déclarations précédentes, Ord(Club) renvoie zéro, Ord(Diamond) renvoie 1, et ainsi de suite.

Types d'intervalle

Un type d'intervalle est une intervalle de valeurs d'un type ordinal appelé type d'hôte. La définition d'un type d'intervalle spécifie la plus petite et la plus grande valeur de l'intervalle ; sa syntaxe est la suivante :

Les deux constantes doivent être du même type ordinal. Les types de sous-intervalle de la forme A..B nécessitent que A soit inférieur ou égal à B. Voici des exemples de types de sous-intervalle :

  1. 0..99
  2. -128..127
  3. Club..Heart

Une variable d'un type de sous-intervalle a toutes les propriétés des variables du type hôte, mais sa valeur d'exécution doit être dans l'intervalle spécifié. Une ambiguïté syntaxique provient de l'autorisation d'expressions constantes alors que le Pascal standard n'autorise que des constantes simples. Considérez les déclarations suivantes :

  1. Const
  2.  X = 50;
  3.  Y = 10;
  4.  
  5. Type
  6.  Color = (Red, Green, Blue);
  7.  Scale = (X - Y) * 2..(X + Y) * 2;

La syntaxe Pascal standard dicte que, si une définition de type commence par une parenthèse, il s'agit d'un type énuméré, tel que le type Color dans l'exemple précédent. L'intention de la déclaration d'échelle est de définir un type de sous-intervalle, cependant. La solution consiste à réorganiser la première expression de sous-intervalle afin qu'elle ne commence pas par une parenthèse, ou à définir une autre constante égale à la valeur de l'expression et à utiliser cette constante dans la définition de type :

  1. Type
  2.  Scale = 2 * (X - Y)..(X + Y) * 2;

Types réels

Un type réel a un ensemble de valeurs étant un sous-ensemble de nombres réels, pouvant être représentés en notation à virgule flottante avec un nombre fixe de chiffres. La notation à virgule flottante d'une valeur comprend normalement trois valeurs - M, B et E - telles que M x BE = N, où B est toujours 2, et M et E sont des valeurs intégrales dans l'intervalle du type réel. Ces valeurs M et E prescrivent en outre la portée et la précision du type réel.

Il existe cinq types de types réels : Real, Single, Double, Extended et Comp. Les types réels diffèrent par l'intervalle et la précision des valeurs qu'ils contiennent, comme indiqué dans le tableau suivant :

Type Intervalle Chiffre significatif Taille en octets
Real 2.9 X 10-39..1.7 X 1038 11 à 12 6
Single 1.5 X 10-45..3.4 X 1038 7 à 8 4
Double 5.0 X 10-324..1.7 X 10308 15 à 16 8
Extended 3.4 X 10-4932..1.1 X 104932 19 à 20 10
Comp -263+1..263-1 19 à 20 8

Le Turbo Pascal prend en charge deux modèles de génération de code pour effectuer des opérations de type réel : virgule flottante logicielle et virgule flottante 80x87. Utilisez la directive du compilateur $N pour sélectionner le modèle approprié. Si aucun 80x87 n'est présent, activez la directive du compilateur $E pour fournir une émulation 80x87 complète dans le logiciel.

Virgule flottante logicielle

Dans l'état {$N-}, étant sélectionné par défaut, le code généré effectue tous les calculs de type réel dans le logiciel en appelant les routines de la bibliothèque d'exécution. Pour des raisons de rapidité et de taille de code, seules les opérations sur les variables de type Real sont autorisées dans cet état. Toute tentative de compilation d'instructions fonctionnant sur les types Single, Double, Extended et Comp génère une erreur.

80x87 virgule flottante

Dans l'état {$N+}, le code généré effectue tous les calculs de type réel à l'aide d'instructions 80x87 et peut utiliser les cinq types réels.

Le Turbo Pascal inclut une bibliothèque d'exécution émulant automatiquement un 80x87 dans le logiciel s'il n'y en a pas. La directive du compilateur $E est utilisée pour déterminer si l'émulateur 80x87 doit être inclus ou non dans un programme.

Types de chaîne de caractères

Une valeur de type chaîne de caractères est une séquence de caractères avec un attribut de longueur dynamique (selon le nombre réel de caractères pendant l'exécution du programme) et un attribut de taille constant de 1 à 255. Un type de chaîne de caractères déclaré sans attribut de taille reçoit l'attribut de taille par défaut 255. La valeur actuelle de l'attribut de longueur est renvoyée par la fonction standard Length.

L'ordre entre deux valeurs de chaîne de caractères quelconques est défini par la relation d'ordre des valeurs de caractère dans les positions correspondantes. Dans deux chaînes de caractères de longueur inégale, chaque caractère de la chaîne de caractères la plus longue sans caractère correspondant dans la chaîne de caractères la plus courte prend une valeur supérieure ou supérieure à ; par exemple, «xs» est supérieur à «x». Les chaînes de caractères nulles ne peuvent être égales qu'à d'autres chaînes de caractères nulles et elles contiennent le moins de valeurs de chaîne de caractères.

Les caractères d'une chaîne de caractères sont accessibles en tant que composants d'un tableau.

Les fonctions standard Low et High peuvent être appliquées à un identificateur de type chaîne de caractères et à une référence variable d'un type chaîne de caractères. Dans ce cas, Low renvoie zéro et High renvoie l'attribut de taille (longueur maximale) de la chaîne de caractères donnée.

Un paramètre variable déclaré à l'aide de l'identificateur OpenString, ou à l'aide du mot-clef String dans l'état {$P+}, est un paramètre de chaîne de caractères ouverte. Les paramètres de chaîne de caractères ouverts permettent de transmettre des variables de chaîne de caractères de tailles différentes à la même procédure ou fonction.

Types structurés

Un type structuré, caractérisé par sa méthode de structuration et par son(ses) type(s) de composante, détient plus d'une valeur. Si un type de composante est structuré, le type structuré résultant a plus d'un niveau de structuration. Un type structuré peut avoir des niveaux de structuration illimités.

En Pascal Standard, le mot emballé dans la déclaration d'un type structuré indique au compilateur de compresser l'entreposage des données, même au prix d'un accès réduit à un composant d'une variable de ce type. Dans Turbo Pascal, cependant, emballé n'a aucun effet ; à la place, l'emballage s'effectue automatiquement chaque fois que cela est possible.

Types de tableaux

Les tableaux ont un nombre fixe de composantes d'un type, le type de composante. Dans le diagramme de syntaxe suivant, le type de composante suit le mot clef OF :

Les types d'index, un pour chaque dimension du tableau, spécifient le nombre d'éléments. Les types d'index valides sont tous des types ordinaux, à l'exception de LongInt et d'intervalles de LongInt. Le tableau peut être indexé dans chaque dimension par toutes les valeurs du type d'index correspondant ; par conséquent, le nombre d'éléments est le produit du nombre de valeurs dans chaque type d'index. Voici un exemple de type tableau :

  1. Array[1..100] of Real

Si le type de composante d'un type de tableau est également un tableau, vous pouvez traiter le résultat comme un tableau de tableaux ou comme un seul tableau multidimensionnel. Par exemple :

  1. Array[Boolean] of Array[1..10] of Array[Size] of Real

est interprété de la même manière par le compilateur que :

  1. Array[Boolean,1..10,Size] of Real

Vous pouvez également exprimer :

  1. Packed Array[1..10] of Packed Array[1..8] of Boolean

comme

  1. Packed Array[1..10,1..8] of Boolean

Vous accédez aux composantes d'un tableau en fournissant à l'identificateur du tableau un ou plusieurs index entre parenthèses. Lorsqu'elles sont appliquées à un identificateur de type tableau ou à une référence de variable d'un type tableau, les fonctions standard Low et High renvoient les limites inférieure et supérieure du type d'index du tableau. Un type tableau de la format :

  1. Packed Array[M..N] of Char

M est inférieur à N est appelé un type de chaîne de caractères compactée (le mot compacté peut être omis car il n'a aucun effet dans Turbo Pascal). Un type de chaîne de caractères compacté a certaines propriétés non partagées par d'autres types de tableau, comme expliqué ci-dessous. Un type tableau de la format :

  1. Array[0..X] of Char

X est un entier positif non zéro est appelé un tableau de caractères de base zéro. Les tableaux de caractères de base zéro sont utilisés pour entreposer des chaînes de caractères terminées par zéro, et lorsque la syntaxe étendue est activée (à l'aide d'une directive de compilateur {$X+}), un tableau de caractères de base zéro est compatible avec une valeur PChar.

Un paramètre déclaré à l'aide du tableau de syntaxe T est un paramètre de tableau ouvert. Les paramètres de tableau ouvert permettent de transmettre des tableaux de tailles différentes à la même procédure ou fonction.

Types d'enregistrement

Un type d'enregistrement comprend un nombre défini de composantes, ou champs, pouvant être de types différents. La déclaration de type d'enregistrement spécifie le type de chaque champ et l'identificateur nommant le champ.

La partie fixe d'un type d'enregistrement présente la liste des champs fixes, en donnant un identificateur et un type pour chacun. Chaque champ contient des informations étant toujours récupérées de la même manière. Voici un exemple de type d'enregistrement :

  1. Type
  2.  TDateRec=Record
  3.   Year:Integer;
  4.   Month:1..12;
  5.   Day:1..31;
  6.  End;

La partie variante illustrée dans le diagramme de syntaxe d'une déclaration de type d'enregistrement distribue l'espace mémoire pour plus d'une liste de champs, de sorte que les informations peuvent être consultées de plusieurs manières. Chaque liste de champs est une variante. Les variantes recouvrent le même espace en mémoire, et tous les champs de toutes les variantes sont accessibles à tout moment :

Vous pouvez voir sur le diagramme que chaque variante est identifiée par au moins une constante. Toutes les constantes doivent être distinctes et d'un type ordinal compatible avec le type de champ de balise. Les champs variables et fixes sont accessibles de la même manière. Un identificateur facultatif, l'identificateur du champ de balise, peut être placé dans la partie variante. Si un identificateur de champ de balise est présent, il devient l'identificateur d'un champ fixe supplémentaire - le champ de balise - de l'enregistrement. Le programme peut utiliser la valeur du champ de balise pour montrer quelle variante est active à un moment donné. Sans champ de balise, le programme sélectionne une variante selon un autre critère.

Voici quelques types d'enregistrement avec variantes :

  1. Type
  2.  TPerson=Record
  3.   Prenom,NomFamille:String[40];
  4.   DateNaissance:TDate;
  5.   Case Citoyen: Boolean of
  6.    True:(
  7.     LieuNaissance:String[40]
  8.    );
  9.    False:(
  10.     Pays:String[20]);
  11.     PortEntree:String[20];
  12.     DateEntree:TDate;
  13.     DateSortie:TDate
  14.    );
  15.   End;
  16.   
  17.   TPolygon=Record
  18.    X,Y:Real;
  19.    Case Kind: Figure of
  20. TRectangle:(Height,Width:Real);
  21.     TTriangle:(Side1,Side2,Angle:Real);
  22. TCircle:(Radius:Real);
  23.   End;

Types d'objets

Un type d'objet est une structure constituée d'un nombre fixe de composantes. Chaque composante est soit un champ, contenant des données d'un type particulier, soit une méthode, effectuant une opération sur l'objet. Semblable à une déclaration de variable, la déclaration d'un champ spécifie le type de données du champ et un identificateur nommant le champ. Semblable à une déclaration de procédure ou de fonction, la déclaration d'une méthode spécifie un entête de procédure, fonction, constructeur ou destructeur.

Un type d'objet peut hériter des composantes d'un autre type d'objet. Si T2 hérite de T1, alors T2 est un descendant de T1 et T1 est un ancêtre de T2. L'héritage est transitif ; c'est-à-dire que si T3 hérite de T2 et que T2 hérite de T1, alors T3 hérite également de T1. Le domaine d'un type d'objet se compose de lui-même et de tous ses descendants.

Le code suivant montre des exemples de déclarations de type d'objet :

  1. Type
  2.  TPoint=Object
  3.    X,Y:Integer;
  4.  End;
  5.  TRectangle=Object
  6.   A,B:TPoint;
  7.   Procedure InitRect(XA,YA,XB,YB:Integer);
  8.   Procedure CopyRect(Var R:TRectangle);
  9.   Procedure MoveRect(DX,DY:Integer);
  10.   Procedure GrowRecf(DX,DY:Integer);
  11.   Procedure IntersectRect(Var R:TRectangle);
  12.   Procedure UnionRect(Var R:TRectangle);
  13.   Function ContainsRect(D:TPoint):Boolean;
  14.  End;   
  15.  
  16.  PString = ^String;
  17.  PField = ^TField;
  18.  TField = Object
  19.   Private
  20.    X,Y,Len:Integer;
  21.    Name:String;
  22.   Public
  23.    Constructor CopyTF(Var F:TField);
  24.    Constructor InitTF(FX,FY,FLen:Integer; FName:String);
  25.    Destructor DoneTF; Virtual;
  26.    Procedure DisplayTF; Virtual;
  27.    Procedure EditTF; Virtual;
  28.    Function GetStrTF:String; Virtual;
  29.    Function PutStrTF(S:String):Boolean; Virtual;
  30.   Private
  31.    Procedure DisplayStrTF(X,Y:Integer;S:String);
  32.  End;
  33.    
  34.  PStrField = ^TStrField;
  35.  TStrField = Object(TField)
  36.   Private
  37.    Value:PString;
  38.   Public
  39.    Constructor Init(FX,FY,FLen:Integer; FName:String);
  40.    Destructor Done; Virtual;
  41.    Function GetStr:String; Virtual;
  42.    Function PutStr(S:String):Boolean; Virtual;
  43.    Function Get:String;
  44.    Procedure Put(S:String);
  45.  End;
  46.  
  47.  PNumField = ^TNumField;
  48.  TNumField = Object(TField)
  49.   Private
  50.    Value,Min,Max:LongInt;
  51.   Public
  52.    Constructor Init(FX,FY,FLen:Integer;FName:String;FMin,FMax:LongInt);
  53.    Function GetStr:String; Virtual;
  54.    Function PutStr(S:String):Boolean; Virtual;
  55.    Function Get:LongInt; 
  56.    Procedure Put(N:LongInt);
  57.  End;
  58.  
  59.  PZipField = ^TZipField;
  60.  TZipField = Object(TNumField)
  61.   Public
  62.    Function GetStr:String; Virtual;
  63.    Function PutStr(S:String):Boolean; Virtual;
  64.  End; 

Contrairement aux autres types, un type d'objet ne peut être déclaré que dans une partie de déclaration de type dans la portée la plus externe d'un programme ou d'une unité. Par conséquent, un type d'objet ne peut pas être déclaré dans une partie de déclaration de variable ou dans une procédure, une fonction ou un bloc de méthode. Le type de composante d'un type de fichier ne peut pas être un type d'objet ou tout type structuré avec un composant de type objet.

Composants et portée

La portée d'un identificateur de composante s'étend sur le domaine de son type d'objet. En outre, la portée d'un identificateur de composante s'étend aux blocs de procédure, de fonction, de constructeur et de destructeur mettant en oeuvre des méthodes du type d'objet et de ses descendants. Pour cette raison, l'orthographe d'un identificateur de composante doit être unique au sein d'un type d'objet et de tous ses descendants et de toutes ses méthodes.

Les identificateurs de composantes déclarés dans la liste des composants suivant immédiatement l'entête de type d'objet et les identificateurs de composantes déclarés dans les sections de composants public n'ont pas de restrictions particulières sur leur portée. En revanche, la portée des identificateurs de composantes déclarés dans les sections de composantes private est limitée au module (programme ou unité) contenant la déclaration de type d'objet. En d'autres termes, les identificateurs de composante private agissent comme des identificateurs de composante publics normaux dans le module contenant la déclaration de type d'objet, mais en dehors du module, tous les identificateurs de composante private sont inconnus et inaccessibles. En plaçant des types d'objets liés dans le même module, ces types d'objets peuvent accéder aux composantes private les uns des autres sans faire connaître les composants private aux autres modules.

Dans une déclaration de type d'objet, un entête de méthode peut spécifier des paramètres du type d'objet déclaré, même si la déclaration n'est pas encore terminée. Dans l'exemple précédent, les méthodes CopyRect, IntersectRect et UnionRect du type TRectangle illustrent cela.

Méthodes

La déclaration d'une méthode dans un type d'objet correspond à une déclaration directe de cette méthode. Cela signifie que quelque part après la déclaration de type d'objet, et dans la même portée que la déclaration de type d'objet, la méthode doit être implémentée par une déclaration de définition.

Lorsqu'une identification unique d'une méthode est requise, un identifiant de méthode qualifié est utilisé. Il se compose d'un identificateur de type objet, suivi d'un point (.), suivi d'un identificateur de méthode. Comme tout autre identificateur, un identificateur de méthode qualifiée peut être précédé d'un identificateur d'unité et d'un point, si nécessaire.

Méthodes virtuelles

Par défaut, les méthodes sont statiques. À l'exception des méthodes constructeur, elles peuvent être rendues virtuelles en incluant une directive virtuelle dans la déclaration de méthode. Le compilateur résout les appels aux méthodes statiques au moment de la compilation. Les appels aux méthodes virtuelles sont résolus au moment de l'exécution ; c'est ce qu'on appelle la liaison tardive.

Si un type d'objet déclare ou hérite de méthodes virtuelles, les variables de ce type doivent être initialisées via un appel de constructeur avant tout appel à une méthode virtuelle. Par conséquent, tout type d'objet déclarant ou héritant de méthodes virtuelles doit également déclarer ou hériter d'au moins une méthode constructeur.

Un type d'objet peut remplacer (redéfinir) n'importe laquelle des méthodes qu'il hérite de ses ancêtres. Si une déclaration de méthode dans un descendant spécifie le même identificateur de méthode qu'une déclaration de méthode dans un ancêtre, alors la déclaration dans le descendant remplace la déclaration dans l'ancêtre. La portée d'une méthode de substitution s'étend sur le domaine du descendant dans lequel elle est introduite, ou jusqu'à ce que l'identificateur de méthode soit à nouveau remplacé.

Un remplacement d'une méthode statique est libre de changer l'entête de la méthode comme bon lui semble. En revanche, une substitution d'une méthode virtuelle doit correspondre exactement à l'ordre, aux types et aux noms des paramètres, ainsi qu'au type du résultat de la fonction, le cas échéant. Le remplacement doit à nouveau inclure une directive virtuelle.

Méthodes dynamiques

Le Turbo Pascal prend en charge une classe supplémentaire de méthodes à liaison tardive appelées méthodes dynamiques. Les méthodes dynamiques diffèrent des méthodes virtuelles uniquement par la manière dont les appels de méthodes dynamiques sont distribués au moment de l'exécution. Pour toutes les autres fins, une méthode dynamique peut être considérée comme équivalente à une méthode virtuelle.

La déclaration d'une méthode dynamique est similaire à celle d'une méthode virtuelle, sauf qu'une déclaration de méthode dynamique doit inclure un index de méthode dynamique juste après le mot-clef Virtual. L'index de méthode dynamique doit être une constante entière dans la plage 1..65535 et il doit être unique parmi les index de méthode dynamique de toute autre méthode dynamique contenue dans le type d'objet ou ses ancêtres. Par exemple :

  1. Procedure FileOpen(Var Msg:TMessage); Virtual 100;

Un remplacement d'une méthode dynamique doit correspondre exactement à l'ordre, aux types et aux noms des paramètres et au type du résultat de la fonction de la méthode ancestrale. Le remplacement doit également inclure une directive virtuelle suivie du même index de méthode dynamique que celui spécifié dans le type d'objet ancêtre.

Instanciation d'objets

Un objet est instancié, ou créé, via la déclaration d'une variable ou d'une constante typée d'un type d'objet, ou en appliquant la procédure New à une variable pointeur d'un type d'objet. L'objet résultant est appelé une instance du type d'objet. Par exemple, étant donné ces déclarations de variables :

  1. Var
  2.  F:TField;
  3.  Z:TZipField;
  4.  FP:PField;
  5.  ZP:PZipField;

Le champ F est une instance de TField et Z est une instance de TZipField. De même, après avoir appliqué New à FP et ZP, FP pointe vers une instance de TField et ZP pointe vers une instance de TZipField. Si un type d'objet contient des méthodes virtuelles, les instances de ce type d'objet doivent être initialisées via un appel de constructeur avant tout appel à une méthode virtuelle. Voici un exemple :

  1. Var
  2.  S:TStrField;
  3. Begin
  4.  S.Init(1, 1, 25, 'Prénom');
  5.  S.Put ('Sylvain');
  6.  S.Display;
  7.   { : }
  8.  S.Done;
  9. End;

Si S.Init n'a pas été appelé, l'appel à S.Display fait échouer cet exemple.

L'affectation à une instance d'un type d'objet n'initialise pas l'instance. Un objet est initialisé par le code généré par le compilateur s'exécutant entre le moment où l'appel du constructeur a lieu et le moment où l'exécution atteint réellement la première instruction du bloc de code du constructeur.

Si une instance d'objet n'est pas initialisée et que la vérification d'intervalle est sur {$R+}, le premier appel à une méthode virtuelle de l'instance d'objet entraîne une erreur d'exécution. Si la vérification d'intervalle est désactivée {$R-}, l'appel d'une méthode virtuelle d'une instance d'objet non initialisée entraîne un comportement indéfini.

La règle d'initialisation requise s'applique également aux instances étant des composantes de types structurés. Par exemple :

  1. Var
  2.  Comment:Array[1..5] of TStrField;
  3.  I:Integer;
  4. Begin
  5.  For I:= 1 to 5 do Comment[I].Init(1, I + 10, 40, 'Commentaire');
  6.  For I:= 1 to 5 do Comment[I].Done;
  7. End;

Pour les instances dynamiques, l'initialisation est généralement associée à l'allocation, et le nettoyage est généralement associé à la désallocation, en utilisant la syntaxe étendue des procédures New et Dispose. Voici un exemple :

  1. Var
  2.  SP:PStrField;
  3. Begin
  4.  New(SP, Init(1, 1, 25, 'Prénom'));
  5.  SP^.Put('Sylvain') ;
  6.  SP^.Display;
  7.    { : }
  8.  Dispose(SP, Done);
  9. End;

Un pointeur vers un type d'objet est compatible avec l'affectation avec un pointeur vers n'importe quel type d'objet ancêtre. Par conséquent, lors de l'exécution d'un programme, un pointeur vers un type d'objet peut pointer vers une instance de ce type ou vers une instance de n'importe quel type descendant.

Par exemple, un pointeur de type PZipField peut être affecté à des pointeurs de type PZipField, PNumField et PField, et pendant l'exécution d'un programme, un pointeur de type PField peut être nul ou pointer sur une instance de TField, TStrField, TNumField, ou TZipField, ou toute autre instance d'un descendant de TField.

Les règles de compatibilité d'affectation de pointeur s'appliquent également aux paramètres variables de type objet. Par exemple, la méthode TField.Copy peut recevoir une instance de TField, TStrField, TNumField, TZipField ou toute autre instance d'un descendant de TField.

Activations de méthode

Une méthode est activée via un appel de fonction ou une instruction de procédure consistant en un indicateur de méthode suivi d'une liste de paramètres actuelles. Ce type d'appel est appelé activation de méthode.

La référence de variable spécifiée dans un indicateur de méthode doit désigner une instance d'un type d'objet et l'identificateur de méthode doit désigner une méthode de ce type d'objet. L'instance désignée par un indicateur de méthode devient un paramètre actuel implicite de la méthode ; il correspond à un paramètre variable formel nommé Self possédant le type d'objet correspondant à la méthode activée.

Pour les méthodes statiques, le type déclaré (au moment de la compilation) de l'instance détermine la méthode à activer. Par exemple, les désignateurs F.Init et Fp^.Init activeront toujours TField.Init car le type déclaré de F et FP^ est TField. Pour les méthodes virtuelles, le type actuel (d'exécution) de l'instance régit la sélection. Par exemple, le désignateur FP^.Display peut activer TField.Display, TStrField.Display, TNumField.Display ou TZipField.Display, selon le type actuel de l'instance pointée par FP.

Dans une instruction With faisant référence à une instance d'un type d'objet, la partie référence de variable d'un indicateur de méthode peut être omise. Dans ce cas, le paramètre implicite Self de l'activation de la méthode devient l'instance référencée par l'instruction With.

De même, au sein d'une méthode, la partie référence de variable d'un indicateur de méthode peut être omise. Dans ce cas, le paramètre implicite Self de l'activation de la méthode devient le Self de la méthode contenant l'appel.

Activations de méthodes qualifiées

Au sein d'une méthode, un appel de fonction ou une instruction de procédure permet à un indicateur de méthode qualifié de désigner l'activation d'une méthode spécifique. Ce type d'appel est appelé activation par méthode qualifiée :

Le type d'objet spécifié dans un désignateur de méthode qualifiée doit être le même que le type d'objet de la méthode englobante ou un ancêtre de celui-ci. Le mot réservé hérité peut être utilisé pour désigner l'ancêtre du type d'objet de la méthode englobante ; hérité ne peut pas être utilisé dans les méthodes d'un type d'objet qui n'a pas d'ancêtre.

Le paramètre implicite Self d'une activation de méthode qualifiée devient le Self de la méthode contenant l'appel. Une activation de méthode qualifiée n'emploie jamais le mécanisme de répartition de méthode virtuelle - l'appel est toujours statique et invoque toujours la méthode spécifiée. Une activation de méthode qualifiée est généralement utilisée dans une méthode de remplacement pour activer la méthode remplacée. En référence aux types déclarés précédemment, voici quelques exemples d'activations de méthodes qualifiées :

  1. Constructor TNumField.Init(FX,FY,FLen:Integer;FName:String;FMin,FMax:LongInt);Begin
  2.  Inherited Init(FX,FY,FLen,FName);
  3.  Value := 0;
  4.  Min := FMin;
  5.  Max := FMax;
  6. End;
  7.  
  8. Function TZipField.PutStr(S:String):Boolean;Begin
  9.  PutStr := (Length(S) = 5) and TNumField.PutStr(S);
  10. End;

Comme le montrent ces exemples, une activation de méthode qualifiée permet à une méthode de substitution de «réutiliser» le code de la méthode qu'elle remplace.

Les types d'ensembles

L'intervalle de valeurs d'un type d'ensemble est l'ensemble de puissance d'un type ordinal particulier (le type de base). L'ensemble de puissance est l'ensemble de tous les sous-ensembles possibles de valeurs du type de base, y compris l'ensemble vide. Par conséquent, chaque valeur possible d'un type d'ensemble est un sous-ensemble des valeurs possibles du type de base. Une variable d'un type ensemble peut contenir de aucune à toutes les valeurs de l'ensemble :

Le type de base ne doit pas avoir plus de 256 valeurs possibles et les valeurs ordinales des limites supérieure et inférieure du type de base doivent être comprises entre 0 et 255. Chaque type d'ensemble peut contenir la valeur [ ], étant appelée l'ensemble vide.

Types de fichier

Un type de fichier consiste en une séquence linéaire de composants du type de composant, pouvant être de n'importe quel type à l'exception d'un type de fichier, de tout type structuré avec une composante de type fichier ou d'un type d'objet. Le nombre de composantes n'est pas défini par la déclaration de type de fichier :

Si le mot OF et le type de composante sont omis, le type désigne un fichier non typé. Les fichiers non typés sont des canaux d'entrée/sortie de bas niveau (entrée/sortie) principalement utilisés pour un accès direct à n'importe quel fichier disque, quel que soit son format interne. Le type de fichier standard Text signifie un fichier contenant des caractères organisés en lignes. Les fichiers texte utilisent des procédures d'entrée/sortie spéciales, étant décrites «Entrée et sortie».

Types de pointeur

Un type pointeur définit un ensemble de valeurs pointant vers des variables dynamiques d'un type spécifié appelé type de base. Une variable de type pointeur contient l'adresse mémoire d'une variable dynamique.

Si le type de base est un identificateur non déclaré, il doit être déclaré dans la même partie de déclaration de type que le type pointeur. Vous pouvez affecter une valeur à une variable pointeur avec la procédure New, l'opérateur @ ou la fonction Ptr. La fonction New alloue une nouvelle zone mémoire dans le tas de l'application pour une variable dynamique et entrepose l'adresse de cette zone dans la variable pointeur. L'opérateur @ dirige la variable pointeur vers la zone mémoire contenant toute variable existante ou point d'entrée de procédure ou de fonction, y compris les variables ayant déjà des identificateurs. La fonction Ptr pointe la variable pointeur vers une adresse mémoire spécifique. Le mot réservé désigne une constante à valeur de pointeur ne pointant vers rien.

Le type Pointer

Le type prédéfini Pointer désigne un pointeur non typé ; c'est-à-dire un pointeur ne pointant vers aucun type spécifique. Les variables de type Pointer ne peuvent pas être déréférencées ; écrire le symbole du pointeur ^ après une telle variable est une erreur. Les pointeurs génériques, cependant, peuvent être transtypés pour permettre le déréférencement. Comme la valeur indiquée par le mot NIL, les valeurs de type Pointer sont compatibles avec tous les autres types de pointeur.

Le type PChar

Le Turbo Pascal a un type prédéfini, PChar, pour représenter un pointeur vers une chaîne de caractères terminée par le zéro. L'unité System déclare PChar comme :

  1. Type PChar = ^Char;

Le Turbo Pascal prend en charge un ensemble de règles de syntaxe étendues pour faciliter la gestion des chaînes terminées par NULL à l'aide du type PChar.

Types de procédure

Le Pascal standard considère les procédures et les fonctions comme des parties de programme pouvant être exécutées via des appels de procédure ou de fonction. Le Turbo Pascal a une vue beaucoup plus large des procédures et des fonctions : il permet aux procédures et aux fonctions d'être traitées comme des entités pouvant être affectées à des variables et transmises en tant que paramètres. De telles actions sont rendues possibles par des types procéduraux. Une déclaration de type procédural spécifie les paramètres et, pour une fonction, le type de résultat.

Essentiellement, la syntaxe pour écrire une déclaration de type procédural est exactement la même que pour écrire un entête de procédure ou de fonction, sauf que l'identificateur après le mot-clef de procédure ou de fonction est omis. Voici quelques exemples de déclarations de type procédural :

  1. Type
  2.  Proc=Procedure;
  3.  SwapProc=Procedure(Var X,Y:Integer);
  4.  StrProc=Procedure(S:String);
  5.  MathFunc=Function(X:Real):Real;
  6.  DeviceFunc=function(Var F:Text):Integer;
  7.  MaxFunc=Function(A,B:Real;F:MathFunc):Real;

Les noms de paramètres dans une déclaration de type procédural sont purement - décoratifs - ils n'ont aucun effet sur la signification de la déclaration. Le Turbo Pascal ne vous permet pas de déclarer des fonctions recevant des valeurs de type procédural ; un résultat de fonction doit être une chaîne de caractères, un réel, un entier, un caractère, un booléen, un pointeur ou une valeur de type énumération définie par l'utilisateur. Mais vous pouvez renvoyer l'adresse d'une procédure ou d'une fonction à l'aide d'un résultat de fonction de type Pointer, puis la castré dans le type de procédure souhaité.

Valeurs procédurales

Une variable de type procédural peut se voir attribuer une valeur procédurale. Les valeurs procédurales peuvent être l'une des suivantes :

Dans le contexte des valeurs procédurales, une déclaration de procédure ou de fonction peut être considérée comme un type spécial de déclaration de constante, la valeur de la constante étant la procédure ou la fonction. Par exemple, étant donné les déclarations suivantes :

  1. Var
  2.  P:SwapProc;
  3.  F:MathFunc;
  4.  
  5. Procedure Swap(Var A,B:Integer);Far;
  6. Var
  7.  Temp:Integer;
  8. Begin
  9.  Temp := A;
  10.  A := B;
  11.  B := Temp;
  12. End;
  13.  
  14. Function Tan(Angle:Real);Far;Begin
  15.  Tan:=Sin(Angle)/Cos(Angle);
  16. End;

les variables P et F peuvent être affectées des valeurs suivantes :

  1. P := Swap;
  2. F := Tan;

et les appels peuvent être passés en utilisant P et F comme suit :

  1. P(I, J); { Équivalent à Swap(I, J) }
  2. X := F(X); { Équivalent à X := Tan(X) }

L'utilisation d'une variable procédurale à laquelle la valeur NIL a été affectée dans une instruction de procédure ou un appel de fonction entraîne une erreur. NIL est destiné à indiquer qu'une variable procédurale n'est pas affectée, et chaque fois qu'il existe une possibilité qu'une variable procédurale soit nulle, les instructions de procédure ou les appels de fonction impliquant cette variable procédurale doivent être surveillés par un test :

  1. If @P <> NIL Then P(I, J);

Notez l'utilisation de l'opérateur @ pour indiquer que P est en cours d'examen plutôt que d'être appelé.

Compatibilité des types

Pour être considérés comme compatibles, les types procéduraux doivent avoir le même nombre de paramètres, et les paramètres dans les positions correspondantes doivent être de types identiques. Enfin, les types de résultats des fonctions doivent être identiques. Les noms de paramètres n'ont aucune importance pour déterminer la compatibilité de type procédural. La valeur NIL est compatible avec n'importe quel type procédural.

Pour être utilisées comme valeurs procédurales, les procédures et fonctions doivent être déclarées avec une directive far ou compilées dans l'état {$F+}. De plus, les procédures et fonctions standard, les procédures et fonctions imbriquées, les méthodes, les procédures et fonctions en ligne et les procédures d'interruption ne peuvent pas être utilisées comme valeurs procédurales. Les procédures et fonctions standard sont celles déclarées par l'unité System, telles que WriteLn, ReadLn, Chr et Ord. Pour utiliser une procédure ou une fonction standard en tant que valeur procédurale, écrivez un coquille autour d'elle. Par exemple, la fonction suivante FSin est compatible avec l'affectation du type MathFunc déclaré ci-dessus :

  1. Function FSin(X:Real):Real;Far;Begin
  2.  FSin:=Sin(X);
  3. End;

Une procédure ou une fonction est imbriquée lorsqu'elle est déclarée dans une autre procédure ou fonction. De telles procédures et fonctions imbriquées ne peuvent pas être utilisées comme valeurs procédurales.

Types identiques et compatibles

Deux types peuvent être identiques, et cette similitude (identité) est obligatoire dans certains contextes. À d'autres moments, les deux types doivent seulement être compatibles ou simplement compatibles avec l'affectation. Ils sont identiques lorsqu'ils sont déclarés avec, ou que leurs définitions découlent du même identificateur de type.

Identité de type

L'identité de type n'est requise qu'entre les paramètres variables réels et formels dans les appels de procédure et de fonction. Deux types - disons, T1 et T2 - sont identiques si l'une des conditions suivantes est vraie : T1 et T2 sont le même identificateur de type ; T1 est déclaré équivalent à un type identique à T2. La deuxième condition implique que T1 n'a pas à être déclaré directement pour être équivalent à T2. Les déclarations de type :

  1. T1 = Integer;
  2. T2 = T1;
  3. T3 = Integer;
  4. T4 = T2;

résultent en T1, T2, T3, T4 et Integer comme des types identiques. Les déclarations de type :

  1. T5 = Set of Integer;
  2. T6 = Set of Integer;

ne rendez pas T5 et T6 identiques car l'ensemble d'entiers n'est pas un identificateur de type. Deux variables déclarées dans la même déclaration, par exemple :

  1. V1,V2:Set of Integer;

sont de types identiques, sauf si les déclarations sont séparées. Les déclarations :

  1. V1:Set of Integer;
  2. V2:Set of Integer;
  3. V3:Integer;
  4. V4:Integer;

signifient que V3 et V4 sont de type identique, mais pas V1 et V2.

Compatibilité des types

La compatibilité entre deux types est parfois requise, comme dans les expressions ou dans les opérations relationnelles. La compatibilité de type est toutefois importante en tant que condition préalable à la compatibilité d'affectation.

La compatibilité de type existe lorsqu'au moins une des conditions suivantes est TRUE :

Compatibilité des affectations

La compatibilité d'affectation est nécessaire lorsqu'une valeur est affectée à quelque chose, comme dans une instruction d'affectation ou lors de la transmission de paramètres de valeur. Une valeur de type T2 est compatible pour l'affectation avec un type T1 (c'est-à-dire que T1 := T2 est autorisé) si l'un des éléments suivants est True :

Une erreur de compilation se produit lorsque la compatibilité des affectations est nécessaire et qu'aucun des éléments de la liste précédente n'est pas vrai.

La partie déclaration de type

Les programmes, procédures, fonctions et méthodes déclarant des types ont une partie déclaration de type. Voici un exemple de partie de déclaration de type :

  1. Type
  2.  TRange = Integer;
  3.  TNumber = Integer;
  4.  TColor = (Red, Green, Blue);
  5.  TCharVal = Ord('A')..Ord('Z');
  6.  TTestIndex = 1..100;
  7.  TTestValue = -99..99;
  8.  TTestList = Array[TTestIndex] of TTestValue;
  9.  PTestList = ^TTestList;
  10.  TDate = Object
  11.   Year:Integer;
  12.   Month:1..12;
  13.   Day:1..31;
  14.   Procedure SetDate(D,M,Y:Integer);
  15.   Function ShowDate:String;
  16.  End;
  17.  
  18.  TMeasureData=Record
  19.   When:TDate;
  20.   Count:TTestIndex;
  21.   Data:PTestList;
  22.  End;
  23.  
  24.  TMeasureList=Array[1..50] of TMeasureData;
  25.  TName = string[80];
  26.  TSex = (Male, Female);
  27.  PPersonData = ATPersonData;
  28.  TPersonData = Record
  29.   Name, FirstName: TName;
  30.   Age: Integer;
  31.   Married: Boolean;
  32.   TFather, TChild, TSibling: PPersonData;
  33.   Case S:TSex of
  34.    Male:(Bearded:Boolean);
  35.    Female:(Pregnant:Boolean);
  36.   End;
  37.  TPersonBuf=Array[0..SizeOf(TPersonData)-1] of Byte;
  38.  TPeople=File of TPersonData;

Dans l'exemple, Range, Number et Integer sont des types identiques. TTestIndex est compatible et compatible avec l'affectation, mais n'est pas identique aux types Number, Range et Integer. Notez l'utilisation d'expressions constantes dans les déclarations de TCharVal et TPersonBuf.



Dernière mise à jour : Dimanche, le 26 décembre 2021