Fonctions virtuelles (C++)

De RAD Studio
Aller à : navigation, rechercher

Remonter à Fonctions virtuelles - Index


Les fonctions virtual permettent aux classes dérivées de mettre en place différentes versions d'une fonction de classe de base. Vous pouvez utiliser le mot clé virtual pour déclarer une fonction virtual dans une classe de base : en déclarant le prototype de fonction de manière habituelle et en préfixant la déclaration du mot clé virtual. Pour déclarer une fonction pure (qui déclare automatiquement une classe abstraite), vous devez préfixer le prototype du mot clé virtual, et définir la fonction égale à zéro.

virtual int funct1(void);       // déclaration de fonction virtuelle
virtual int funct2(void) = 0;   // déclaration de fonction pure

Une déclaration de fonction ne peut pas fournir à la fois une définition et un spécificateur pur.

Exemple :

struct C {
    virtual void f() { } = 0;   // incorrect
};

La seule syntaxe légale permettant de fournir un corps est :

struct TheClass
{
  virtual void funct3(void) = 0;
};
virtual void TheClass::funct3(void)
{
   // Il y a du code ici
};

Remarque :  Voir Classes abstraites pour de plus amples informations sur les fonctions virtuelles pures.

Lorsque vous définissez des fonctions virtual, gardez à l'esprit les indications suivantes :

  • Elles ne peuvent être que des fonctions membres.
  • Elles peuvent être déclarées comme friend d'une autre classe.
  • Elles ne peuvent pas être un membre statique.

Vous n'avez pas à redéfinir une fonction virtual dans une classe dérivée. Vous pouvez fournir une définition dans la classe de base de façon que tous les appels accèdent à la fonction de base.

Pour redéfinir une fonction virtual dans une classe dérivée, le nombre et le type d'arguments doivent être identiques dans la déclaration de la classe de base et dans celle de la classe dérivée. Les fonctions virtual redéfinies dont seul le type des valeurs renvoyées est différent sont traitées plus loin. Une fonction redéfinie remplace la fonction de la classe de base.

Vous pouvez aussi déclarer les fonctions int Base::Fun(int) et int Derived::Fun(int) même quand elles ne sont pas virtual. Dans ce cas, on dit que int Derived::Fun(int) cache toutes les autres versions de Fun(int) existant dans n'importe quelle classe de base. En outre, si une classe dérivée définit d'autres versions de Fun() (c'est-à-dire des versions de Fun() portant des signatures différentes), celles-ci sont dites surchargées.

Types de valeurs renvoyées par les fonctions virtuelles

En règle générale, lorsque vous redéfinissez une fonction virtual, vous ne pouvez pas modifier seulement le type de valeur renvoyée. Lors de la redéfinition d'une fonction virtual, la nouvelle définition (dans certaines classes dérivées) doit correspondre exactement au type de renvoi et aux paramètres formels de la déclaration initiale. Si deux fonctions portant le même nom ont des paramètres formels différents, C++ les considère comme différentes et passe outre le mécanisme de fonction virtual.

En revanche, certaines fonctions virtuelles d'une classe de base ont une version de redéfinition dans une classe dérivée dont le type des valeurs renvoyées est différent de la fonction redéfinie. Cela est possible uniquement lorsque les deux conditions suivantes sont respectées :

  • La fonction virtual remplacée renvoie un pointeur ou une référence de la classe de base.
  • La fonction de redéfinition renvoie un pointeur ou une référence de la classe dérivée.

Supposons une classe de base B contenant une fonction virtual vf, et la classe D dérivée de B contenant une fonction vf du même type ; si vf est appelée pour un objet d de D, l'appel est D::vf(), même si l'accès se fait par un pointeur ou une référence à B. Par exemple,

struct X {};// Classe de base
struct Y : X {};// Classe dérivée
struct B {
   virtual void vf1();
   virtual void vf2();
   virtual void vf3();
   void f();
   virtual X* pf();// Le type renvoyé est un pointeur sur base
  // Cela peut être redéfini
};
class D : public B {
public:
   virtual void vf1();// Spécificateur virtuel légal mais redondant
   void vf2(int);// Pas de spécificateur virtuel, puisqu'une autre liste
 //  d'arguments est utilisée Cela cache B::vf2()
// char vf3();// Illégal: seul le type de renvoi change
   void f();
   Y*   pf();// La fonction redéfinie diffère seulement
   // par le type renvoyé. Elle renvoie un pointeur sur
 //  la classe dérivée
};
void extf()
{
   D d;// Instancie un objet D
   B* bp = &d;// Conversion standard de D* vers B*
  // Initialise bp avec la table de fonctions fournie
// pour l'objet d. S'il n'existe aucune entrée
  // dans la table d pour une fonction a,
     //  utilisez la fonction dans la table B
   bp–>vf1(); // Appelle D::vf1
   bp–>vf2();  // Appelle B::vf2 puisque D's vf2 a des arguments différents
   bp–>f();   // Appelle B::f (non virtuelle)
   X* xptr = bp–>pf();// Appelle D::pf() et convertit le résultat
   //  en un pointeur sur X
   D* dptr = &d;
   Y* yptr = dptr–>pf();// Appelle D::pf() et initialise yptr
   //  Aucune autre conversion n'est faite
}

La fonction de redéfinition vf1 dans D est automatiquement virtual. Le spécificateur virtual est utilisable avec une déclaration de fonction de redéfinition dans la classe dérivée. Si d'autres classes doivent être dérivées de D, le mot clé virtual est obligatoire. Si aucune autre classe ne doit être dérivée de D, l'emploi de virtual est redondant.

L'interprétation d'un appel de fonction virtual dépend du type de l'objet pour lequel il y a appel ; avec des appels de fonctions non virtuelles, l'interprétation dépend uniquement du type du pointeur ou de la référence désignant l'objet concerné par l'appel.

Les fonctions virtual exigent le prix de leur polyvalence : chaque objet de la classe dérivée possède un pointeur sur une table de fonctions pour permettre la sélection de la fonction adéquate au moment de l'exécution (liaison en temps réel)..

Voir aussi