Fonction virtuelle
En programmation orientée objet, une fonction virtuelle est une fonction définie dans une classe (méthode) qui est destinée à être redéfinie dans les classes qui en héritent. Dans la plupart des langages, soit toutes les méthodes sont automatiquement virtuelles (Java, Swift…), soit le mot clé virtual
est utilisé pour indiquer que la méthode d'une classe est virtuelle (C++, Delphi, Free Pascal…).
Une fonction ne possédant qu'une déclaration, sans code implémenté, est dite abstraite ou virtuelle pure.
Les méthodes virtuelles pures sont destinées à être définies dans les classes dérivées.
exemple
Delphi
type
{ ----------------------------------- }
{ Classe TForme. }
{ ----------------------------------- }
TForme = class
public
function aire():integer; virtual;
end;
{ ----------------------------------- }
{ Classe TTriangle dérivée de TForme. }
{ ----------------------------------- }
TTriangle = class(TForme)
public
function aire():integer; virtual;
end;
{ ----------------------------------- }
{ Classe TCercle dérivée de TForme. }
{ ----------------------------------- }
TCercle = class(TForme)
public
function aire():integer; virtual;
end;
C++
//---------------------------------------------------------------------------
// Classe Forme.
//---------------------------------------------------------------------------
class Forme
{
public:
// La méthode est virtuelle pure.
virtual unsigned int aire() = 0;
};
//---------------------------------------------------------------------------
// Classe Triangle dérivée de Forme.
//---------------------------------------------------------------------------
class Triangle : public Forme
{
public:
virtual unsigned int aire();
};
//---------------------------------------------------------------------------
// Classe Cercle dérivée de Forme.
//---------------------------------------------------------------------------
class Cercle : public Forme
{
public:
virtual unsigned int aire();
};
Java
/**
* Classe Forme.
**/
public abstract class Forme {
/**
* La méthode est virtuelle pure.
**/
public abstract int aire();
};
/**
* Classe Triangle dérivée de Forme.
**/
public class Triangle extends Forme {
/**
* Implémentation de la méthode.
**/
@Override
public int aire() {
return ...; // calcul de l'aire à partir des champs définis
}
};
/**
* Classe Cercle dérivée de Forme.
**/
public class Cercle extends Forme {
/**
* Implémentation de la méthode.
**/
@Override
public int aire() {
return ...; // calcul de l'aire à partir des champs définis
}
};
La méthode aire() est redéfinie par chaque classe dérivée. En effet, la manière de calculer l'aire d'une forme dépend du type de celle-ci.
Il suffit qu'une classe possède une méthode virtuelle pure pour qu'elle soit dite abstraite.
On ne peut pas instancier une classe abstraite car cela n'aurait aucun sens. Par exemple : on peut instancier un objet « triangle » qui hérite de la classe de base abstraite « forme géométrique » mais on ne peut pas créer un objet « forme géométrique », trop général.
Surcharge explicite
Certains langages imposent (Swift, C#, ..) ou conseillent (Java, ...) de placer un mot-clé pour indiquer qu'une méthode redéfinit (override) la méthode parente virtuelle. Cela documente le code (la méthode surcharge une méthode virtuelle parente) et permet au compilateur de verifier que c'est bien le souhait de celui qui écrit la méthode.
Cela est particulièrement intéressant dans le cas d'utilisation d'une bibliothèque externe.
- Considérons une classe C d'une bibliothèque en version 1 qui définit une unique méthode m1.
- Un programme utilise cette bibliothèque et définit une classe D qui hérite de C et qui définit la méthode m2.
Jusque là, rien de particulier.
- La bibliothèque passe en version 2 et définit maintenant une méthode m2 (absolument identique en signature à celle de D).
- Considérons que la bibliothèque en version 2 est absolument compatible avec tous les appels faits avec la version 1.
Et pourtant, si le programme est recompilé avec la bibliothèque en version 2, il y a de grandes chances que ça ne fonctionne plus correctement. Car la méthode m2 de D redéfinit la méthode m2 de C et il serait très étonnant qu'elle possède un code identique.
Si un mot-clé de surcharge est imposé, le programme ne compile plus car le compilateur découvre que la méthode m2 de D surcharge la méthode m2 de C sans le dire explicitement. L'informaticien découvre donc l'erreur à la compilation et peut corriger son programme :
- soit ne pas utiliser la version 2,
- soit renommer la méthode m2 de D,
- soit rendre la méthode m2 compatible avec les desiderata de C.