HOWTO · C++

Surcharge de fonction VS Override en C++

Cet article compare la surcharge de fonctions et la redéfinition de fonctions en C++.

Sur cette page

La surcharge de fonctions et la redéfinition de fonctions répondent à des besoins différents en C++. La surcharge de fonctions donne le même nom à plusieurs fonctions d’une même portée, mais avec des listes de paramètres différentes ; le compilateur choisit celle qui correspond aux arguments de l’appel. La redéfinition de fonctions permet à une classe dérivée de remplacer une fonction virtual héritée par une implémentation correspondante ; un appel effectué par une référence ou un pointeur vers la classe de base est alors dirigé vers l’objet dérivé à l’exécution.

Sens de la surcharge et de la redéfinition en C++

La surcharge consiste à choisir entre des fonctions qui diffèrent par leurs paramètres. La redéfinition consiste à remplacer un comportement dans une hiérarchie d’héritage. Le type de retour, à lui seul, ne distingue pas une surcharge, et la redéfinition exige une fonction virtual dans la classe de base. Le spécificateur override demande au compilateur de vérifier que la déclaration de la classe dérivée correspond bien à une fonction virtuelle de la classe de base.

Fonctionnement de la surcharge de fonctions

Déclarez, dans une même portée, des fonctions portant le même nom, mais avec des listes de paramètres distinctes. Ces listes peuvent différer par le nombre ou le type des paramètres. Lors d’un appel, la résolution de surcharge s’appuie sur les arguments et les types de paramètres des fonctions candidates. Le type de retour ne sert pas à choisir une surcharge : deux déclarations qui ne diffèrent que par leur type de retour ne constituent donc pas un ensemble de surcharges valide.

Exécutez cet exemple :

Le code et les libellés ou sorties affichés dans cette section restent en anglais.

#include <iostream>

class Formatter {
public:
    void show(int value) const {
        std::cout << "integer: " << value << '\n';
    }

    void show(double value) const {
        std::cout << "decimal: " << value << '\n';
    }
};

int main() {
    Formatter formatter;
    formatter.show(7);
    formatter.show(2.5);
}
integer: 7
decimal: 2.5

Les éléments d’exécution fournis confirment que cet exemple s’est exécuté correctement et a produit cette sortie. Les arguments entier et décimal sélectionnent deux surcharges différentes de show. Lorsque vous ajoutez une surcharge, veillez à ce que sa liste de paramètres soit réellement différente et que les appels restent sans ambiguïté.

Fonctionnement de la redéfinition de fonctions

Une classe de base déclare une fonction virtual ; une classe dérivée fournit une fonction portant le même nom, avec une liste de paramètres identique ainsi que des qualificatifs et un type de retour compatibles. Ajoutez override à la déclaration de la classe dérivée afin qu’une incompatibilité déclenche un diagnostic à la compilation. Lors d’un appel par une référence ou un pointeur vers la classe de base, la répartition virtuelle sélectionne la redéfinition finale correspondant à l’objet.

Exécutez cet exemple :

Le code et les libellés ou sorties affichés dans cette section restent en anglais.

#include <iostream>

class Message {
public:
    virtual void print() const {
        std::cout << "message\n";
    }
};

class Alert : public Message {
public:
    void print() const override {
        std::cout << "alert\n";
    }
};

int main() {
    Alert alert;
    const Message& message = alert;
    message.print();
}
alert

Les éléments d’exécution fournis confirment que cet exemple s’est exécuté correctement et a produit cette sortie. Bien que message soit de type classe de base, il désigne un objet Alert ; l’appel virtuel utilise donc Alert::print. Une classe de base peut aussi déclarer une fonction virtuelle pure avec = 0 ; une telle classe est abstraite et ne peut pas être instanciée tant que des classes dérivées concrètes n’implémentent pas ses fonctions virtuelles pures.

Surcharge et redéfinition : principales différences

Aspect Surcharge de fonctions Redéfinition de fonctions
Objectif Proposer des opérations portant le même nom pour des arguments différents Spécialiser le comportement hérité pour un type dérivé
Exigence Des listes de paramètres distinctes dans une même portée Un héritage et une fonction virtual correspondante dans la classe de base
Sélection Résolution de surcharge à la compilation à partir des arguments de l’appel Répartition virtuelle à l’exécution par une référence ou un pointeur vers la classe de base
Vérification utile S’assurer que les appels désignent la surcharge voulue Ajouter override pour vérifier que la déclaration correspond

Les surcharges peuvent exister sans héritage ni fonction virtuelle. Les redéfinitions reposent sur l’héritage et la répartition virtuelle ; elles ne sont pas sélectionnées en passant une liste d’arguments différente.

Erreurs courantes et comment les reconnaître

Une fonction d’une classe dérivée portant le même nom qu’une fonction de base ne la redéfinit pas automatiquement. Si la fonction de base n’est pas virtuelle, la déclaration dérivée masque le nom hérité au lieu d’activer la répartition virtuelle. Si un paramètre, le qualificatif const ou un autre élément du type de fonction ne correspond pas, la redéfinition attendue peut échouer ; écrire override révèle cette incompatibilité à la compilation.

N’essayez pas de créer une surcharge en changeant uniquement le type de retour. Évitez aussi de transmettre des objets dérivés par valeur lorsque vous attendez un comportement polymorphe : leur copie dans un objet de base tronque la partie dérivée. Utilisez une référence ou un pointeur vers la classe de base pour préserver la répartition dynamique. Pour gérer la durée de vie des objets, privilégiez les objets automatiques ou les pointeurs intelligents ; si vous supprimez un objet dérivé au moyen d’un pointeur vers la classe de base, celle-ci doit avoir un destructeur virtuel.