Pascal - Cours

Vous avez vu que les objets Pascal présentent certaines caractéristiques du paradigme orienté objet. Ils implémentent l'encapsulation, le masquage des données et l'héritage, mais ils ont également des limites. Par exemple, les objets Pascal ne participent pas au polymorphisme. Les classes sont donc largement utilisées pour implémenter un comportement orienté objet approprié dans un programme, en particulier le logiciel basé sur l'interface graphique.

Une classe est définie presque de la même manière qu'un objet, mais est un pointeur vers un objet plutôt que vers l'objet lui-même. Techniquement, cela signifie que la classe est allouée sur le tas d'un programme, alors que l'objet est alloué sur la pile. En d'autres termes, lorsque vous déclarez une variable de type objet, elle prendra autant d'espace sur la pile que la taille de l'objet, mais lorsque vous déclarez une variable de type classe, elle prendra toujours la taille d'un pointeur sur la pile. Les données de classe réelles seront sur le tas.

Définition des classes Pascal

Une classe est déclarée de la même manière qu'un objet, en utilisant la déclaration de type. La forme générale d'une déclaration de classe est la suivante -

type class-identifier = class  
   private
      field1 : field-type;  
      field2 : field-type;  
        ...
   
   public
      constructor create();
      procedure proc1;  
      function f1(): function-type;
end;  
var classvar : class-identifier;

Il vaut la peine de noter les points importants suivants -

  • Les définitions de classe doivent relever uniquement de la partie déclaration de type du programme.

  • Une classe est définie à l'aide du class mot-clé.

  • Les champs sont des éléments de données qui existent dans chaque instance de la classe.

  • Les méthodes sont déclarées dans la définition d'une classe.

  • Il existe un constructeur prédéfini appelé Createdans la classe Root. Chaque classe abstraite et chaque classe concrète est un descendant de Root, donc toutes les classes ont au moins un constructeur.

  • Il existe un destructeur prédéfini appelé Destroydans la classe Root. Chaque classe abstraite et chaque classe concrète est un descendant de Root, donc toutes les classes ont au moins un destructeur.

Définissons une classe Rectangle qui a deux membres de données de type entier - longueur et largeur et certaines fonctions membres pour manipuler ces membres de données et une procédure pour dessiner le rectangle.

type
   Rectangle = class
   private
      length, width: integer;
   
   public
      constructor create(l, w: integer);
      procedure setlength(l: integer);
      function getlength(): integer;
      procedure setwidth(w: integer);
      function getwidth(): integer;
      procedure draw;
end;

Écrivons un programme complet qui créerait une instance d'une classe de rectangle et dessinerait le rectangle. C'est le même exemple que nous avons utilisé lors de la discussion des objets Pascal. Vous constaterez que les deux programmes sont presque identiques, avec les exceptions suivantes -

  • Vous devrez inclure la directive {$ mode objfpc} pour utiliser les classes.

  • Vous devrez inclure la directive {$ m +} pour utiliser les constructeurs.

  • L'instanciation de classe est différente de l'instanciation d'objet. La seule déclaration de la variable ne crée pas d'espace pour l'instance, vous utiliserez le constructeur create pour allouer de la mémoire.

Voici l'exemple complet -

{$mode objfpc} // directive to be used for defining classes
{$m+}		   // directive to be used for using constructor

program exClass;
type
   Rectangle = class
   private
      length, width: integer;
   
   public
      constructor create(l, w: integer);
      procedure setlength(l: integer);
      
      function getlength(): integer;
      procedure setwidth(w: integer);
      
      function getwidth(): integer;
      procedure draw;
end;
var
   r1: Rectangle;

constructor Rectangle.create(l, w: integer);
begin
   length := l;
   width := w;
end;

procedure Rectangle.setlength(l: integer);
begin
   length := l;
end;

procedure Rectangle.setwidth(w: integer);
begin
   width :=w;
end;

function Rectangle.getlength(): integer;
begin
   getlength := length;
end;

function Rectangle.getwidth(): integer;
begin
   getwidth := width;
end;

procedure Rectangle.draw;
var
   i, j: integer;
begin
   for i:= 1 to length do
   begin
      for j:= 1 to width do
         write(' * ');
      writeln;
   end;
end;

begin
   r1:= Rectangle.create(3, 7);
   
   writeln(' Draw Rectangle: ', r1.getlength(), ' by ' , r1.getwidth());
   r1.draw;
   r1.setlength(4);
   r1.setwidth(6);
   
   writeln(' Draw Rectangle: ', r1.getlength(), ' by ' , r1.getwidth());
   r1.draw;
end.

Lorsque le code ci-dessus est compilé et exécuté, il produit le résultat suivant -

Draw Rectangle: 3 by 7
* * * * * * *
* * * * * * *
* * * * * * *
Draw Rectangle: 4 by 6
* * * * * * 
* * * * * * 
* * * * * * 
* * * * * *

Visibilité des membres du groupe

La visibilité indique l'accessibilité des membres de la classe. Les membres de la classe Pascal ont cinq types de visibilité -

Sr. Non Visibilité et accessibilité
1

Public

Ces membres sont toujours accessibles.

2

Private

Ces membres ne sont accessibles que dans le module ou l'unité contenant la définition de classe. Ils sont accessibles depuis l'intérieur des méthodes de classe ou depuis l'extérieur d'elles.

3

Strict Private

Ces membres ne sont accessibles qu'à partir des méthodes de la classe elle-même. Les autres classes ou classes descendantes de la même unité ne peuvent pas y accéder.

4

Protected

C'est la même chose que private, sauf que ces membres sont accessibles aux types descendants, même s'ils sont implémentés dans d'autres modules.

5

Published

C'est la même chose qu'un public, mais le compilateur génère des informations de type qui sont nécessaires pour le streaming automatique de ces classes si le compilateur est dans l'état {$ M +}. Les champs définis dans une section publiée doivent être de type classe.

Constructeurs et destructeurs pour les classes Pascal

Les constructeurs sont des méthodes spéciales, qui sont appelées automatiquement chaque fois qu'un objet est créé. Nous tirons donc pleinement parti de ce comportement en initialisant de nombreuses choses via des fonctions constructeur.

Pascal fournit une fonction spéciale appelée create () pour définir un constructeur. Vous pouvez passer autant d'arguments que vous le souhaitez dans la fonction constructeur.

L'exemple suivant créera un constructeur pour une classe nommée Books et initialisera le prix et le titre du livre au moment de la création de l'objet.

program classExample;

{$MODE OBJFPC} //directive to be used for creating classes
{$M+} //directive that allows class constructors and destructors
type
   Books = Class 
   private 
      title : String; 
      price: real;
   
   public
      constructor Create(t : String; p: real); //default constructor
      
      procedure setTitle(t : String); //sets title for a book
      function getTitle() : String; //retrieves title
      
      procedure setPrice(p : real); //sets price for a book
      function getPrice() : real; //retrieves price
      
      procedure Display(); // display details of a book
end;
var
   physics, chemistry, maths: Books;

//default constructor 
constructor Books.Create(t : String; p: real);
begin
   title := t;
   price := p;
end;

procedure Books.setTitle(t : String); //sets title for a book
begin
   title := t;
end;

function Books.getTitle() : String; //retrieves title
begin
   getTitle := title;
end;

procedure Books.setPrice(p : real); //sets price for a book
begin
   price := p;
end;

function Books.getPrice() : real; //retrieves price
begin
   getPrice:= price;
end;

procedure Books.Display();
begin
   writeln('Title: ', title);
   writeln('Price: ', price:5:2);
end;

begin 
   physics := Books.Create('Physics for High School', 10);
   chemistry := Books.Create('Advanced Chemistry', 15);
   maths := Books.Create('Algebra', 7);
   
   physics.Display;
   chemistry.Display;
   maths.Display;
end.

Lorsque le code ci-dessus est compilé et exécuté, il produit le résultat suivant -

Title: Physics for High School
Price: 10
Title: Advanced Chemistry
Price: 15
Title: Algebra
Price: 7

Comme le constructeur implicite nommé create, il existe également une méthode destructrice implicite destroy à l'aide de laquelle vous pouvez libérer toutes les ressources utilisées dans la classe.

Héritage

Les définitions de classe Pascal peuvent éventuellement hériter d'une définition de classe parent. La syntaxe est la suivante -

type
childClas-identifier = class(baseClass-identifier) 
< members >
end;

L'exemple suivant fournit une classe de romans, qui hérite de la classe Books et ajoute plus de fonctionnalités en fonction de l'exigence.

program inheritanceExample;

{$MODE OBJFPC} //directive to be used for creating classes
{$M+} //directive that allows class constructors and destructors

type
   Books = Class 
   protected 
      title : String; 
      price: real;
   
   public
      constructor Create(t : String; p: real); //default constructor
      
      procedure setTitle(t : String); //sets title for a book
      function getTitle() : String; //retrieves title
      
      procedure setPrice(p : real); //sets price for a book
      function getPrice() : real; //retrieves price
      
      procedure Display(); virtual; // display details of a book
end;
(* Creating a derived class *)

type
   Novels = Class(Books)
   private
      author: String;
   
   public
      constructor Create(t: String); overload;
      constructor Create(a: String; t: String; p: real); overload;
      
      procedure setAuthor(a: String); // sets author for a book
      function getAuthor(): String; // retrieves author name
      
      procedure Display(); override;
end;
var
   n1, n2: Novels;

//default constructor 
constructor Books.Create(t : String; p: real);
begin
   title := t;
   price := p;
end;

procedure Books.setTitle(t : String); //sets title for a book
begin
   title := t;
end;

function Books.getTitle() : String; //retrieves title
begin
   getTitle := title;
end;

procedure Books.setPrice(p : real); //sets price for a book
begin
   price := p;
end;

function Books.getPrice() : real; //retrieves price
begin
   getPrice:= price;
end;

procedure Books.Display();
begin
   writeln('Title: ', title);
   writeln('Price: ', price);
end;

(* Now the derived class methods  *)
constructor Novels.Create(t: String);
begin
   inherited Create(t, 0.0);
   author:= ' ';
end;

constructor Novels.Create(a: String; t: String; p: real);
begin
   inherited Create(t, p);
   author:= a;
end;

procedure Novels.setAuthor(a : String); //sets author for a book
begin
   author := a;
end;

function Novels.getAuthor() : String; //retrieves author
begin
   getAuthor := author;
end;

procedure Novels.Display();
begin
   writeln('Title: ', title);
   writeln('Price: ', price:5:2);
   writeln('Author: ', author);
end;

begin 
   n1 := Novels.Create('Gone with the Wind');
   n2 := Novels.Create('Ayn Rand','Atlas Shrugged', 467.75);
   n1.setAuthor('Margaret Mitchell');
   n1.setPrice(375.99);
   n1.Display;
   n2.Display;
end.

Lorsque le code ci-dessus est compilé et exécuté, il produit le résultat suivant -

Title: Gone with the Wind
Price: 375.99
Author: Margaret Mitchell
Title: Atlas Shrugged
Price: 467.75
Author: Ayn Rand

Il vaut la peine de noter les points importants suivants -

  • Les membres de la classe Livres ont protected visibilité.

  • La classe Novels a deux constructeurs, donc le overload L'opérateur est utilisé pour la surcharge de fonction.

  • La procédure Books.Display a été déclarée virtual, afin que la même méthode de la classe Novels puisse override il.

  • Le constructeur Novels.Create appelle le constructeur de classe de base à l'aide du inherited mot-clé.

Interfaces

Les interfaces sont définies pour fournir un nom de fonction commun aux implémenteurs. Différents développeurs peuvent implémenter ces interfaces en fonction de leurs besoins. Vous pouvez dire que les interfaces sont des squelettes, qui sont implémentés par les développeurs. Voici un exemple d'interface -

type  
   Mail = Interface  
      Procedure SendMail;  
      Procedure GetMail;  
   end;  
   
   Report = Class(TInterfacedObject,  Mail)  
      Procedure SendMail;  
      Procedure GetMail;  
   end;

Veuillez noter que, lorsqu'une classe implémente une interface, elle doit implémenter toutes les méthodes de l'interface. Si une méthode d'une interface n'est pas implémentée, alors le compilateur donnera une erreur.

Classes abstraites

Une classe abstraite est une classe qui ne peut pas être instanciée, seulement héritée. Une classe abstraite est spécifiée en incluant le mot symbole abstract dans la définition de classe, comme ceci -

type
   Shape = ABSTRACT CLASS (Root)
      Procedure draw; ABSTRACT;
      ...
   end;

Lors de l'héritage d'une classe abstraite, toutes les méthodes marquées abstract dans la déclaration de classe du parent doivent être définies par l'enfant; de plus, ces méthodes doivent être définies avec la même visibilité.

Mot-clé statique

Déclarer des membres de classe ou des méthodes comme statiques les rend accessibles sans avoir besoin d'une instanciation de la classe. Un membre déclaré comme statique n'est pas accessible avec un objet de classe instancié (bien qu'une méthode statique le puisse). L'exemple suivant illustre le concept -

program StaticExample;
{$mode objfpc}
{$static on}
type
   myclass=class
      num : integer;static;
   end;
var
   n1, n2 : myclass;
begin
   n1:= myclass.create;
   n2:= myclass.create;
   n1.num := 12;
   writeln(n2.num);
   n2.num := 31;
   writeln(n1.num);
   writeln(myclass.num);
   myclass.num := myclass.num + 20;
   writeln(n1.num);
   writeln(n2.num);
end.

Lorsque le code ci-dessus est compilé et exécuté, il produit le résultat suivant -

12
31
31
51
51

Vous devez utiliser la directive {$ static on} pour utiliser les membres statiques.