Newsletter Developpez.com

Inscrivez-vous gratuitement au Club pour recevoir
la newsletter hebdomadaire des développeurs et IT pro

FAQ PascalConsultez toutes les FAQ

Nombre d'auteurs : 10, nombre de questions : 400, dernière mise à jour : 12 septembre 2016  Ajouter une question

 

Bienvenue dans la F.A.Q. Pascal !

Celle-ci rassemble les réponses aux questions les plus fréquemment posées sur le langage Pascal et tous ses outils de programmation. Si elle n'a pas pour vocation de répondre à toutes les interrogations possibles, elle reste une bonne base de connaissances sur le Pascal, et ne demande qu'à être enrichie par vos expériences personnelles.

Nous vous invitons à proposer vos propres questions/réponses directement dans la FAQ ou, si vous souhaitez apporter une modification à une question/réponse existante, à la poster dans le fil de discussion renseigné ci-dessous.

Nous vous souhaitons une bonne lecture !

L'équipe Pascal.

Commentez


SommaireLes différents compilateursFree PascalFree Pascal et la Programmation Orientée ObjetNotions fondamentales (19)
précédent sommaire suivant
 

Pour avoir accès à la Programmation Orientée Objet avec Free Pascal, vous devez activer l’une des trois options suivantes :

  • {$mode objfpc} ;
  • {$mode delphi} ;
  • {$mode MacPas}.


N.B. : La première option est incluse automatiquement dans le squelette de l’application lorsque vous la créez via Lazarus.

Mis à jour le 10 août 2016 gvasseur58

Une classe est avant tout un type qui regroupe des données (appelées champs) et des opérations (appelées méthodes) sur ces données. La classe décrit par conséquent une structure.

Exemple :

Code pascal : Sélectionner tout
1
2
3
4
5
6
7
type 
  TMyClass = class 
    private 
      fMyField: Integer; 
    public 
      property MyProperty: Integer read fMyField write fMyField; 
  end;

Mis à jour le 10 août 2016 gvasseur58

La déclaration d'une classe en Pascal comprend deux parties :

  • la déclaration proprement dite qui fournit le squelette de la classe ;
  • l'implémentation de la classe avec la définition des méthodes introduites par elle.


Le fait de séparer la définition de l'implémentation présente l'avantage de rendre plus compact le code de description de la classe. En revanche, le programmeur doit naviguer entre la définition et l'implémentation, ce qui peut être source de perte de temps [N.B. : les EDI comme Lazarus fournissent des combinaisons de touches permettant des allers-retours rapides entre les deux parties]

Exemple :

Code pascal : Sélectionner tout
NomDeLObjet.NomDeLaMethode

Notez que pour être utilisée, une méthode ordinaire doit appartenir à une classe qui a été instanciée. Autrement dit, la première partie de l'expression, celle qui précède le point, doit être un objet valide.

Exemple :
Code pascal : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  
interface 
  
type 
  TMyClass = class // déclaration 
    private 
       fMyInt: Integer; 
   public 
      function DoubleInt: Integer; 
      property MyInt: Integer read fMyInt write fMyInt; 
  end; 
  
implementation 
  
function TMyClass.DoubleInt; // implémentation 
begin 
  Result := MyInt * 2; 
end;

Mis à jour le 10 août 2016 gvasseur58

L'habitude veut que le nom d'une classe soit précédé d'un T. Par exemple :

Code pascal : Sélectionner tout
1
2
3
 
type 
  TMyClass = class;

Cependant, ce préfixe n'a rien d'obligatoire. Ce T correspond à la première lettre de Type et constitue par conséquent un repère très utile pour le programmeur. Il est ainsi vivement conseillé de l'utiliser de manière systématique pour une relecture facilitée des codes sources.

Mis à jour le 10 août 2016 gvasseur58

La déclaration d'une classe doit se faire dans la section typede la partie à la portée la plus large d'un programme ou d'une unité, ou dans une autre classe (type imbriqué).

Exemple dans une unité :

Code pascal : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
unit animal; 
  
{$mode objfpc}{$H+} 
  
interface 
  
uses 
  Classes, SysUtils; 
  
type 
  { TAnimal } 
  
  TAnimal = class 
  [...] 
  end;

Exemple de déclaration d'une classe dans une autre classe :

Code pascal : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
type 
  
  { TMyClass } 
  
  TMyClass = class 
    type 
       { TMyNestedClass } 
      TMyNestedClass = class // classe imbriquée ! 
      strict private 
        fMyNestedField: string; 
       [...] 
      public 
        property MyNestedField: string read GetMyNestedField write SetMyNestedField; 
      end; 
  private 
      fMyNestedClass: TMyNestedClass; 
      function GetMyProperty: string; 
  public 
     [...] 
      property MyProperty: string read GetMyProperty; 
  end;

Il est en revanche impossible de déclarer une classe dans une procédure ou une fonction.

Exemple :

Code pascal : Sélectionner tout
1
2
3
4
5
6
7
8
procedure MyProc; 
  type 
     TMyClass = class // erreur de compilation ! 
        [...] 
     end; 
begin 
  [...] 
end;

Mis à jour le 10 août 2016 gvasseur58

Un objet est une variable du type défini par une classe. Au lieu de variable, on parle plus souvent d'instance : en effet, en Pascal, il faut créer un objet afin de l'utiliser et le détruire à la fin de son utilisation.

Exemple :

Code pascal : Sélectionner tout
1
2
3
4
5
6
7
var 
  MyObject: TMyClass; 
[...] 
MyObject := TMyClass.Create; 
MyObject.DoSomething; 
[...] 
MyObject.Free;

N.B. : Lorsque vous déclarez une variable de type classe, vous n'allouez qu'un emplacement sur la pile (stack) qui correspond à la taille d'un pointeur sur un objet. Ce dernier est créé sur le tas (heap) aux dimensions beaucoup plus généreuses.

Mis à jour le 10 août 2016 gvasseur58

L'instance d'une classe est tout simplement un objet, c'est-à-dire l'incarnation dans la mémoire de la structure décrite par la classe. Une instance est obtenue par l'utilisation d'un constructeur, généralement baptisé Create.

La création d'un objet est souvent appelée l'instanciation, autrement dit l’action qui consiste à allouer de la mémoire pour l’objet et à renvoyer un pointeur vers l’adresse de son implémentation.

Mis à jour le 10 août 2016 gvasseur58

Afin d'être instanciée, une classe a besoin d'être créée avec un constructeur (constructor). Par défaut, ce dernier porte le nom de Create, mais il vous est possible de le nommer à votre guise et de lui adjoindre des paramètres.

Exemple :

Code pascal : Sélectionner tout
1
2
3
4
var 
  MyClass: TMyClass; 
[...] 
  MyClass := TMyClass.Create;

N.B. : Il est important de remarquer que l'instanciation se fait à partir du nom de la classe. Createse rapporte donc à la classe et non à l'objet, ce qui se comprend si l'on se souvient que l'objet n'existe pas encore !

Mis à jour le 10 août 2016 gvasseur58

À partir du moment où un objet a été créé, une variable appelée Selfest définie implicitement pour chaque méthode de cet objet. Cette variable renvoie une référence aux données de l’objet. Son utilisation la plus fréquente est de servir de paramètre à une méthode ou à une routine qui a besoin de cette référence.

Par exemple, voici le code nécessaire à la création d'un bouton :

Code pascal : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
MonButton := TButton.Create(Self); // création avec un propriétaire 
with MonButton do 
begin 
  Height := 10; 
  Left := 10; 
  Top := 10; 
  Width := 100; 
  Caption := 'Ouvrir...'; 
  OnClick := @ButtonClick; 
  Parent := Self; // le parent permet l'affichage 
end;

Mis à jour le 10 août 2016 gvasseur58

Les champs ou attributs d'une classe décrivent la structure d'une classe. Ils sont l'équivalent d'une variable, mais à l'intérieur d'une classe.

Code pascal : Sélectionner tout
1
2
3
4
5
6
7
type 
  TMyClass = class 
    private 
      fMyStField: string; 
      fMyIntField: Integer; 
  [...] 
  end;

Notez que les noms des champs sont souvent préfixés d'un f ou F pour Field et que l'accès au champ se fait la plupart du temps par le biais d'une propriété.

Mis à jour le 10 août 2016 gvasseur58

L'encapsulation est le concept fondamental de la Programmation Orientée Objet. Il s'agit de protéger toutes les données au sein d'une classe : l'interface restera ainsi identique même si le traitement à l'intérieur de la classe aura été modifié.

Voici une classe rudimentaire :

Code pascal : Sélectionner tout
1
2
3
4
5
6
7
8
9
type 
  TMyClass = class 
  strict private 
    fMyField: Integer; 
    function GetMyField: Integer; 
    procedure SetMyField(AValue: Integer); 
 public 
    property MyField: Integer read GetMyField write SetMyField; 
end;

Le champ fMyFieldest encapsulé dans la classe TMyClass. Il est ainsi protégé de toute manipulation directe.

En général, même si Free Pascal laisse la liberté d'une manipulation directe des champs, seul l'accès à travers une méthode ou une propriété devrait être autorisé pour une encapsulation véritable.

Mis à jour le 17 août 2016 gvasseur58

La portée répond à la question : qui est autorisé à voir cet élément et de ce fait à l'utiliser ?

Si l'on appelle élément un champ ou une méthode d'une classe, les règles suivantes s'appliqueront :

  • strict private : l'élément n'est visible que par un élément de la même classe dans l'unité de la déclaration ;
  • private: l'élément n'est visible que par un élément présent dans l'unité de la déclaration ;
  • strict protected : l'élément n'est utilisable que par un descendant de la classe (donc une classe dérivée), qu'il soit dans l'unité de la classe ou dans une autre unité y faisant référence ;
  • protected: l'élément n'est utilisable que par un descendant de la classe, qu'il soit dans l'unité de la classe ou dans une autre unité y faisant référence, ou par une autre classe présente dans l'unité de la classe ;
  • public: l'élément est accessible partout et par tous ;
  • published: l'élément est accessible partout et par tous, et comprend des informations particulières lui permettant de s'afficher dans l'inspecteur d'objet de Lazarus.


Voici un exemple de déclaration d'une classe :

Code pascal : Sélectionner tout
1
2
3
4
5
6
7
8
9
type 
  TMyClass = class 
  strict private 
    fMyFIeld: string; 
  protected 
    procedure MyProc(const AValue: string); 
  public 
    property MyProp: string read fMyField write fMyField; 
  end;

Les directives de portée sont facultatives. Par défaut, une section est de type public.

Mis à jour le 17 août 2016 gvasseur58

Souvenez-vous tout d'abord que vous produisez des boîtes noires dans lesquelles l'utilisateur introduira des données pour en récupérer d'autres ou pour provoquer certains comportements comme un affichage, une impression, etc. Le niveau d'encapsulation repose par conséquent sur une règle bien admise qui est de ne montrer que ce qui est strictement nécessaire. Par conséquent, choisissez la plupart du temps le niveau d'encapsulation le plus élevé possible pour chaque élément.

Aidez-vous ensuite de ces quelques repères généraux :

  • une section strict private abrite des champs et des méthodes qui servent d'outils de base (l'utilisateur de votre classe n'aura jamais besoin de se servir d'eux directement) ;
  • une section privatepermet à d'autres classes de la même unité de partager des informations ;
  • les variantes de protectedpermettent surtout des redéfinitions de méthodes lors de la création de sous-classes ;
  • la section publicest la portée par défaut, qui n'a pas besoin de se faire connaître puisqu'elle s'offre à la première sollicitation venue !
  • publishedsera un outil précieux lors de l'intégration de composants dans la palette de Lazarus.

Mis à jour le 17 août 2016 gvasseur58

La zone mémoire occupée par l’objet étant mise à zéro dès sa création, il n’est pas nécessaire d’initialiser les champs et les propriétés si vous souhaitez qu’ils prennent leur valeur nulle par défaut : chaîne vide, nombre à zéro, pointeur à nil…

Cependant, initialiser les champs malgré tout est une bonne habitude qui vous évitera bien des déboires lors de la recherche d'erreurs : le code produit n'en sera pas alourdi et vous aurez une vision directe de la valeur par défaut de vos champs.

Mis à jour le 10 août 2016 gvasseur58

Il est possible de dériver des sous-classes d’une classe existante qui hériteront de toutes les fonctionnalités de leur parent. Ce mécanisme s’appelle l’héritage.

Non seulement la classe dérivée saura exécuter les tâches qui lui sont propres, mais elle saura aussi, sans aucune ligne de code supplémentaire à écrire, exécuter les tâches de son ancêtre.

Voici un exemple de sous-classe héritant d'une classe ancêtre :

Code pascal : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
type 
  TMyClass = class 
  string private 
    fMyProp: string; 
  public 
    procedure MyProc(AValue: string); 
    property MyProp: string read fMyProp read fMyProp;  
  end; 
  
  TMySubClass = class(TMyClass) 
  public 
    function NewFunction: Integer; 
  end;

Dans cet exemple, un objet de type TMySubClasspourra utilisé la procédure MyProcde son ancêtre TMyClass, ainsi que la propriété MyProp. De plus, il introduira la nouvelle fonction NewFunction.

N.B. : Une classe donnée ne peut avoir qu’un unique parent, mais autant de descendants que nécessaire.

Mis à jour le 18 août 2016 gvasseur58

En héritant de toutes les propriétés et méthodes publiques de son ancêtre, une classe peut légitimement occuper sa place si elle le souhaite. C’est ce qu’on appelle le polymorphisme qui est une conséquence directe de l’héritage : un objet d’une classe donnée peut prendre la forme de ses ancêtres.

Dans l'exemple suivant, la variable Stacceptera comme paramètre tous les descendants de TStrings, y compris TStringList, se montrant par conséquent très polyvalente.

Code pascal : Sélectionner tout
1
2
3
4
5
6
7
procedure Afficher(St: TStrings);  
var 
  LItem: string; 
begin 
  for LItem in Sts do 
    writeln(LItem); 
end;

Avec un composant MyMemode type TMemo, une écriture comme celle-ci serait correcte :

Code pascal : Sélectionner tout
Afficher(MyMemo.Lines);

Mis à jour le 18 août 2016 gvasseur58

L'opérateur Is vérifie qu’un objet est bien du type d’une classe déterminée et renvoie une valeur booléenne (True ou False).

Exemple :

Code pascal : Sélectionner tout
1
2
if (Rantanplan is TChien) then // Rantanplan est-il un chien ? 
  Result := 'Il s’’agit d’’un chien’;

N.B : Is est souvent associé à As.

Mis à jour le 18 août 2016 gvasseur58

L'opérateur As force un objet à prendre la forme d’une classe déterminée : si cette transformation (appelée transtypage) est impossible du fait de l’incompatibilité des types, une erreur est déclenchée.

Dans l'exemple suivant, si UnAnimal est de type TChien, la méthode Aboyer est exécutée, sinon une erreur est déclenchée :

Code pascal : Sélectionner tout
(UnAnimal as TChien).Aboyer;

L'opérateur As est souvent utilisé avec l'autre opérateur Is. Il est ainsi possible de tester le type de l'objet avant de le forcer à prendre une forme particulière :

Code pascal : Sélectionner tout
1
2
if (UnAnimal is TChien) then // l’objet est-il du type voulu ? 
  (UnAnimal as TChien).Aboyer; // si oui, transtypage avant d’exécuter la méthode

Mis à jour le 18 août 2016 gvasseur58

Pour dévoiler la généalogie d'une classe, vous utiliserez une méthode de classe nommée ClassParent qui fournit un pointeur vers la classe parente de la classe actuelle. Cette méthode de classe est définie par TObject. Vous remonterez dans les générations jusqu’à ce que ce pointeur soit à nil, c’est-à-dire jusqu’à ce qu’il ne pointe sur rien.

La portion de code suivante affiche cette généalogie dans un TMemo nommé mmoDisplay :

Code pascal : Sélectionner tout
1
2
3
4
5
6
7
8
procedure TMainForm.Display(AClass: TClass); 
begin 
  repeat 
    mmoDisplay.Lines.Add(AClass.ClassName); 
    AClass := AClass.ClassParent; 
  until AClass = nil; 
  mmoDisplay.Lines.Add(''); 
end;

Mis à jour le 24 août 2016 gvasseur58

Proposer une nouvelle réponse sur la FAQ

Ce n'est pas l'endroit pour poser des questions, allez plutôt sur le forum de la rubrique pour ça


Réponse à la question

Liens sous la question
précédent sommaire suivant
 

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2017 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

 
Responsables bénévoles de la rubrique Pascal : Gilles Vasseur - Alcatîz -