Programmation C-C++/C++ : La couche objet/Méthodes virtuelles

Un livre de Wikilivres.
Cours de C/C++
^
C++ : La couche objet
Généralités
Extension de la notion de type du C
Déclaration de classes en C++
Encapsulation des données
Héritage
Classes virtuelles
Fonctions et classes amies
Constructeurs et destructeurs
Pointeur this
Données et fonctions membres statiques
Surcharge des opérateurs
Des entrées - sorties simplifiées
Méthodes virtuelles
Dérivation
Méthodes virtuelles pures - Classes abstraites
Pointeurs sur les membres d'une classe

Livre original de C. Casteyde

Les méthodes virtuelles n'ont strictement rien à voir avec les classes virtuelles, bien qu'elles utilisent le même mot clé virtual. Ce mot clé est utilisé ici dans un contexte et dans un sens différent.

Nous savons qu'il est possible de redéfinir les méthodes d'une classe mère dans une classe fille. Lors de l'appel d'une fonction ainsi redéfinie, la fonction appelée est la dernière fonction définie dans la hiérarchie de classe. Pour appeler la fonction de la classe mère alors qu'elle a été redéfinie, il faut préciser le nom de la classe à laquelle elle appartient avec l'opérateur de résolution de portée (::).

Bien que simple, cette utilisation de la redéfinition des méthodes peut poser des problèmes. Supposons qu'une classe B hérite de sa classe mère A. Si A possède une méthode x appelant une autre méthode y redéfinie dans la classe fille B, que se passe-t-il lorsqu'un objet de classe B appelle la méthode x ? La méthode appelée étant celle de la classe A, elle appellera la méthode y de la classe A. Par conséquent, la redéfinition de y ne sert à rien dès qu'on l'appelle à partir d'une des fonctions d'une des classes mères.

Une première solution consisterait à redéfinir la méthode x dans la classe B. Mais ce n'est ni élégant, ni efficace. Il faut en fait forcer le compilateur à ne pas faire le lien dans la fonction x de la classe A avec la fonction y de la classe A. Il faut que x appelle soit la fonction y de la classe A si elle est appelée par un objet de la classe A, soit la fonction y de la classe B si elle est appelée pour un objet de la classe B. Le lien avec l'une des méthodes y ne doit être fait qu'au moment de l'exécution, c'est-à-dire qu'on doit faire une édition de liens dynamique.

Le C++ permet de faire cela. Pour cela, il suffit de déclarer virtuelle la fonction de la classe de base qui est redéfinie dans la classe fille, c'est-à-dire la fonction y. Cela se fait en faisant précéder par le mot clé virtual dans la classe de base.

Exemple 8-25. Redéfinition de méthode de classe de base[modifier | modifier le wikicode]

Problème sans méthode virtuelle[modifier | modifier le wikicode]

#include <iostream>

using namespace std;

// Définit la classe de base des données.

class DonneeBase
{
protected:
    int Numero;   // Les données sont numérotées.
    int Valeur;   // et sont constituées d'une valeur entière
                  // pour les données de base.
public:
    void Entre(void);       // Entre une donnée.
    void MiseAJour(void);   // Met à jour la donnée.
};

void DonneeBase::Entre(void)
{
    cin >> Numero;          // Entre le numéro de la donnée.
    cout << endl;
    cin >> Valeur;          // Entre sa valeur.
    cout << endl;
    return;
}

void DonneeBase::MiseAJour(void)
{
    Entre();                // Entre une nouvelle donnée
                            // à la place de la donnée en cours.
    return;
}

/* Définit la classe des données détaillées. */

class DonneeDetaillee : private DonneeBase
{
    int ValeurEtendue;      // Les données détaillées ont en plus
                            // une valeur étendue.

public:
    void Entre(void);       // Redéfinition de la méthode d'entrée.
};

void DonneeDetaillee::Entre(void)
{
    DonneeBase::Entre();    // Appelle la méthode de base.
    cin >> ValeurEtendue;  // Entre la valeur étendue.
    cout << endl;
    return;
}

Si d est un objet de la classe DonneeDetaillee, l'appel de d.Entre() ne causera pas de problème. En revanche, l'appel de d.MiseAJour() ne fonctionnera pas correctement, car la méthode Entre() appelée dans MiseAJour sera la méthode de la classe DonneeBase, et non celle redéfinie dans DonneeDetaille.

Résolution avec une méthode virtuelle[modifier | modifier le wikicode]

Il faut déclarer la méthode Entre() comme une fonction virtuelle; il n'est nécessaire de le faire que dans la classe de base. Celle-ci doit donc être déclarée comme suit :

class DonneeBase
{
protected:
    int Numero;
    int Valeur;

public:
    virtual void Entre(void);   // Fonction virtuelle.
    void MiseAJour(void);
};

Cette fois, la fonction Entre appelée dans MiseAJour est soit la fonction de la classe DonneeBase, si MiseAJour est appelée pour un objet de classe DonneeBase, soit celle de la classe DonneeDetaille si MiseAJour est appelée pour un objet de la classe DonneeDetaillee.

Conclusion[modifier | modifier le wikicode]

En résumé, les méthodes virtuelles sont des méthodes qui sont appelées selon la vraie classe de l'objet qui l'appelle. Les objets qui contiennent des méthodes virtuelles peuvent être manipulés en tant qu'objets des classes de base, tout en effectuant les bonnes opérations en fonction de leur type. Ils apparaissent donc comme étant des objets de la classe de base et des objets de leur classe complète indifféremment, et on peut les considérer soit comme les uns, soit comme les autres. Un tel comportement est appelé polymorphisme (c'est-à-dire qui peut avoir plusieurs aspects différents). Nous verrons une application du polymorphisme dans le cas des pointeurs sur les objets.