next up previous contents index
suivant: Indications bibliographiques monter: main précédent: Programmation à objets   Table des matières   Index


Objets: champs fonctionnels dans les structures

Pour simuler la programmation à objets en C, une deuxième solution consiste à faire de surface, non une fonction, mais un champ fonctionnel intégré dans la définition d'un type Objet et à faire en sorte que les objets des classes dérivées Point, Cercle et Rectangle contiennent un Objet, sous forme d'un champ super :


struct Objet{
  double (*surface)(Objet *);
};

struct Point {
  Objet super;
  double x,y;
};

struct Cercle {
  Objet super;
  Point *centre;
  double rayon;
};

Pour chaque classe d'objet, on définit ensuite une fonction de calcul de la surface, toutes ces fonctions prenant un argument de type Objet :


double surfaceCercle(Objet *o) {
  double r = ((Cercle *)o)->rayon;
  return M_PI * r * r;
}

Ces fonctions surfacePoint, surfaceCercle et surfaceRectangle ne sont pas destinées à être appelées directement, mais seulement à être affectées, dans chaque constructeur, au champ fonctionnel surface de super, de sorte que l'objet << sait >> quelle méthode de calcul de la surface doit lui être appliquée :


Cercle *newCercle(Point *p, double r) {
  Cercle *c = new Cercle;
  ((Objet *)c)->surface = surfaceCercle;
  c->centre = p;
  c->rayon = r;
  return c;
}

L'utilisation d'un Cercle en tant qu'Objet se fait par une coercition (Objet *), comme dans la solution présentée au § 103 ; c'est maintenant le champ fonctionnel surface qui est appelé :


  Objet
    *c = (Objet *)newCercle(newPoint(0,0), 1);
  double sc = (c->surface)(c);

Ceci fonctionne également grâce à la propriété d'allocation ordonnée des champs des structures. Ainsi, le premier champ de Cercle est aussi le premier champ d'Objet, et c'est l'adresse de la fonction surface.

Cette solution illustre quelques caractéristiques du style à objet. D'abord, chacun des types Point, Cercle et Rectangle étend le type Objet par l'addition de nouveaux champs : c'est la notion d'extension des classes, ou de sous-classage. Dans le cas présent, ceci permet de considérer Cercle comme un sous-type d'Objet, au sens où toute opération sur le type Objet peut être faite aussi sur le type Cercle, de la même façon que int peut être considéré comme un sous-type de double.

Les méthodes sont intégrées à la définition des classes, au lieu d'être des fonctions indépendantes, ce qui assure une certaine modularité. La méthode surface de la classe Objet est virtuelle, au sens où ce champ fonctionnel ne reçoit de valeur que dans les sous-classes d'Objet. Enfin, l'invocation de cette méthode se fait par une liaison tardive, au sens où la détermination de la fonction (surfacePoint, ...) qui sera effectivement invoquée ne peut être faite qu'à l'exécution.

Il ne s'agit que d'une simulation en C++ sans classes, approximative, maladroite voire dangereuse, de certaines caractéristiques du style à objets. Les classes et objets de C++ permettent de bénéficier de façon beaucoup plus simple et sûre de ces diverses caractéristiques. On commence par définir le type abstrait Objet, en déclarant seulement que tout Objet dispose d'une méthode surface, qui sera définie dans des types concrets Point, Cercle, etc.


struct Objet
{
  virtual double surface() = 0;     // méthode abstraite
};

Chaque type concret est défini comme dérivant d'Objet (ce qui fait qu'il contiendra de façon implicite un champ de type Objet, équivalent au super du § 103. Chaque type définit également un constructeur (de même nom que le type) et sa propre méthode surface :


struct Point : Objet                // struct dérivé d'Objet
{
  double x, y;                      // deux champs
  Point(double x, double y) {       // définition de constructeur
    this->x = x;                    // this designe l'objet courant
    this->y = y;
  }
  double surface() {                // définition de méthode
    return 0;
  }
};

struct Cercle : Objet               // struct dérivé d'Objet
{
  Point *centre;
  double rayon;
  Cercle(Point *p, double r) {
    this->centre = p;
    this->rayon = r;
  }
  double surface() {
    double r = this->rayon;
    return M_PI * r * r;
  }
};

struct Rectangle : Objet            // struct dérivé d'Objet 
{
  Point *sg, *id;
  Rectangle(Point *p, Point *q) { ... }
  double surface() { ... }
};

On peut alors déclarer un Objet*, l'initialiser par l'adresse d'un Cercle, appeler la méthode surface sur cet objet, qui se trouve être celle définie dans Cercle :


int main() 
{
  Point *z = new Point(0,0);              // création d'un Point
  Objet *c = new Cercle(z, 1);            // création d'un Cercle
  cout << c->surface() << endl;           // appel de la méthode

  return 0;
}

Cet exemple d'un programme C++ avec objets donne un aperçu de quelques unes des techniques que ce langage offre au programmeur. L'utilisation judicieuse de ces techniques, requise pour le développement de systèmes logiciels complexes, reste cependant assez difficile - et une affaire de goût.


next up previous contents index
suivant: Indications bibliographiques monter: main précédent: Programmation à objets   Table des matières   Index
R Lalement