Exood4 Studio - Video Game Development, Toulouse (France)
Exood4 Studios Exood4 Tutorials

   
L a n g a g e   C++

L e s   f o n c t i o n s   v i r t u e l l e s






Conversion implicite d'un objet d'une classe dérivée


point a;
pointforme b;

a = b; // affectation légale
b = a; // incorrect

point *pa;
pointforme *pb;

pa = pb; // correct
pb = pa; // incorrect


Typage statique des objets


point a, *pa;
pointforme b, *pb;

pa = &a;
pb = &b;

a.afficher ();
pa->afficher (); // méthode afficher de la classe point

b.afficher ();
pb->afficher (); // méthode afficher de la classe pointforme

pa = pb;
pa->afficher (); // méthode afficher de la classe point


Fonction virtuelle et typage dynamique


class point
{
private :
 ...

public :
 virtual void afficher ();
};

class pointforme : public point
{
private :
 ...

public :
 void afficher ();
}

 Grâce à la fonction virtuelle afficher() de la classe point, le dernier appel pa->afficher() de l'exemple précédent utilisera la fonction afficher de la classe pointforme.

point a, *pa;
pointforme b, *pb;

pa = &a;
pb = &b;

a.afficher ();
pa->afficher (); // méthode afficher de la classe point

b.afficher ();
pb->afficher (); // méthode afficher de la classe pointforme

pa = pb;
pa->afficher (); // méthode afficher de la classe pointforme


Fonction virtuelle pour éviter des redondances de code


 Jusqu'à présent nous déclarons les méthodes afficher() de chaque classe comme ceci :

void point::afficher ()
{
 cout << abscisse << "," << ordonnee << "\n";
}

void pointforme::afficher ()
{
 point::afficher ();
 cout << forme << "\n";
}

void pointcolore::afficher ()
{
 point::afficher ();
 cout << couleur << "\n";
}

 Il est donc nécessaire de répéter à chaque fois l'appel à la méthode afficher() de la classe de base point en utilisant l'opérateur de résolution de portée ::

 Les fonctions virtuelles permettent d'éviter cette répétition de code :

//////////////////////////////////////////////////////////////////////
// Déclaration de la classe point
class point
{
private:
 ...

public :
 void afficher ();
 virtual void identifier ();
};

// Définition de la nouvelle méthode afficher de la classe point
void point::afficher ()
{
 cout << abscisse << "," << ordonnee << "\n";
 identifier ();
}

// Définition de la méthode identifier de la classe point
// (méthode qui sera réécrite par les classes dérivées)
void point::identifier ()
{
}

//////////////////////////////////////////////////////////////////////
// Déclaration de la classe pointforme
class pointforme : public point
{
private :
 ...

public :
 void identifier (); // méthode identifier de la classe pointforme
};

// Re-définition dans la classe pointforme de la méthode identifier de la classe point
void pointforme::identifier ()
{
 cout << forme << "\n";
}

//////////////////////////////////////////////////////////////////////
// Déclaration de la classe pointcolore
class pointcolore : public point
{
private :
 ...

public :
 void identifier (); // méthode identifier de la classe pointcolore
};

// Re-définition dans la classe pointcolore de la méthode identifier de la classe point
void pointcolore::identifier ()
{
 cout << couleur << "\n";
}

point p1;
pointcolore p2;
pointforme p3;

p1.afficher (); // 0.0,0.0
p2.afficher (); // 0.0,0.0 1
p3.afficher (); // 0.0,0.0 *


Fonction virtuelle pure et classe abstraite





class classe_abstraite
{
public :
 virtual void afficher () = 0; // méthode virtuelle pure
};

 Si l'on a une méthode virtuelle pure dans une classe, la classe est dite abstraite. Une classe abstraite ne peut jamais être instanciée. Les classes dérivées doivent définir le corps de la méthode virtuelle pure.

//////////////////////////////////////////////////////////////////////
// Déclaration de la classe point
class point : public classe_abstraite
{
private :
 float abscisse, ordonnee;

public :
 ...
 void afficher () { cout << abscisse << "," << ordonnee; }
};

//////////////////////////////////////////////////////////////////////
// Déclaration de la classe vecteur3
class vecteur3 : public classe_abstraite
{
private :
 float x, y, z;

public :
 ...
 void afficher () { cout << x << "," << y << "," << z; }
}

//////////////////////////////////////////////////////////////////////
// Déclaration de la classe matrice2
class matrice2 : public classe_abstraite
{
private :
 float m[2][2];

public :
 ...
 void afficher () { cout << m[0][0] << "," << m[0][1] << "," << m[1][0] << "," << m[1][1]; }
}

 Utilisation :

void main ()
{
 classe_abstraite *t[3];

 t[0] = new point(3.5, 5.2);
 t[1] = new vecteur3(3.5, 6.2, 7.0);
 t[2] = new matrice2(5.0, 5.0, 5.0, 5.0);

 for (int i = 0; i < 3; i++)
 {
 t[i]->afficher (); // La bonne méthode afficher() sera automatiquement reconnue
 }
}


Polymorphisme


1) Polymorphisme d'objet

 On parle de polymorphisme d'objet lorsque tout objet instance d'une classe ancêtre peut être remplacé par un objet d'une classe descendante de la classe ancêtre.


Exemple :



// Déclaration de la classe polygone
class polygone
{
private :
 ...

public :
 ...
};

// Déclaration de la classe rectangle
class rectangle : public polygone
{
private :
 ...

public :
 ...
};

// Déclaration de la classe triangle
class triangle : public polygone
{
private :
 ...

public :
 ...
};

 Soit tableauPolygones un tableau d'objet polygone :

polygone tableauPolygones[10];

 Dans le tableau tableauPolygones on peut mémoriser des polygones, mais aussi des triangles et des rectangles. Nous avons donc plusieurs formes possibles pour tableauPolygones[i]. Il s'agit donc ici de polymorphisme d'objets.


2) Polymorphisme de méthode

 De façon similaire, le polymorphisme de méthode apparaît lorsque l'on est confronté à plusieurs défintions d'une même méthode. Chaque méthode étant spécifique à l'objet (nous avons déjà rencontré ce cas pour la fonction identifier de la classe point) ...


Exemple :

// Déclaration de la classe polygone
class polygone
{
private :
 ...

public :
 virtual float perimetre (); // somme des longueurs des côtés du polygone

 ...
};
...

// Déclaration de la classe rectangle
class rectangle : public polygone
{
private :
 float longueur, largeur;
 ...

public :
 // périmètre d'un rectangle
 float perimetre () { return (2*(longueur + largeur)); }

 ...
};

// Déclaration de la classe triangle
class triangle : public polygone
{
private :
 float base, adjacent, oppose;
 ...

public :
 // périmètre d'un triangle
 float perimetre () { return (base + adjacent + oppose); }

 ...
};

 En reprenant l'exemple de tableauPolygones nous obtiendrons pour tableauPolygones[i] le calcul du périmètre du polygone correspondant à l'objet mémorisé. Nous avons plusieurs méthodes utilisées dans le tableau tableauPolygones. Il s'agit donc de polymorphisme de méthode.



  Retour en haut de page Page suivante