Redéfinition des méthodes
Fait partie de la section Programmation Orientée Objet du Journey C++ de Coddy. Leçon 51 sur 104.
La redéfinition de méthode se produit lorsqu'une classe dérivée fournit sa propre implémentation d'une méthode qui existe déjà dans la classe de base. Cela permet aux classes dérivées de personnaliser le comportement hérité tout en conservant la même signature de méthode.
Pour redéfinir une méthode, définissez simplement une méthode dans la classe dérivée avec exactement le même nom, le même type de retour et les mêmes paramètres :
class Animal {
public:
void speak() {
std::cout << "Some sound" << std::endl;
}
};
class Dog : public Animal {
public:
void speak() {
std::cout << "Woof!" << std::endl;
}
};
Dog d;
d.speak(); // Sortie : Woof!La version de la classe dérivée masque la version de la classe de base. Cependant, vous pouvez toujours accéder à la méthode de la classe de base en utilisant l'opérateur de résolution de portée :
class Dog : public Animal {
public:
void speak() {
Animal::speak(); // Appeler la version de la classe de base
std::cout << "Woof!" << std::endl;
}
};Il existe une limitation importante avec cette forme de base de redéfinition (overriding). Lorsque vous accédez à un objet dérivé via un pointeur ou une référence de classe de base, c'est la version de la classe de base qui est appelée à la place :
Dog d;
Animal* ptr = &d;
ptr->speak(); // Résultat : Some sound (pas Woof !)Cela se produit parce que le compilateur détermine quelle méthode appeler en se basant sur le type du pointeur, et non sur le type réel de l'objet. Pour parvenir à un véritable polymorphisme d'exécution où la méthode correcte est appelée en fonction de l'objet réel, vous aurez besoin de fonctions virtuelles, que nous aborderons dans la prochaine leçon.
Défi
FacileConstruisons un système de notifications qui illustre la redéfinition de méthodes en action. Vous allez créer une classe de base Notifier et deux notificateurs spécialisés qui personnalisent la manière dont les messages sont transmis.
Vous organiserez votre code sur trois fichiers :
Notifier.h: définissez une classe de baseNotifieravec :- un membre protégé
std::string recipient - un constructeur qui accepte un nom de destinataire et le stocke
- une méthode publique
send(const std::string& message)qui affiche :Sending to <recipient>: <message>
- un membre protégé
EmailNotifier.h: définissez une classeEmailNotifierqui hérite publiquement deNotifier:- un constructeur qui accepte un destinataire et le transmet à la classe de base
- redéfinissez la méthode
send()pour appeler d'abord la version de la classe de base à l'aide deNotifier::send(), puis afficher :[Email sent successfully]
main.cpp: lisez deux entrées (chacune sur une ligne distincte) :- Nom du destinataire (chaîne de caractères)
- Contenu du message (chaîne de caractères)
Définissez une classe
SMSNotifierdirectement dans main.cpp qui hérite publiquement deNotifier:- un constructeur qui accepte un destinataire et le transmet à la classe de base
- redéfinissez la méthode
send()pour afficher :SMS to <recipient>: <message>(en remplaçant complètement le comportement de base sans l'appeler)
Créez un objet de chaque type de notificateur (
Notifierde base,EmailNotifieretSMSNotifier) en utilisant le même destinataire. Appelezsend()sur chacun avec le message saisi, en affichant une ligne de séparation entre chacun :--- Base Notifier --- <output> --- Email Notifier --- <output> --- SMS Notifier --- <output>
Par exemple, avec les entrées Alice et Hello there! :
--- Base Notifier ---
Sending to Alice: Hello there!
--- Email Notifier ---
Sending to Alice: Hello there!
[Email sent successfully]
--- SMS Notifier ---
SMS to Alice: Hello there!Remarquez comment EmailNotifier étend le comportement de base en appelant d'abord la méthode parente, tandis que SMSNotifier la remplace complètement par sa propre implémentation. N'oubliez pas les gardes d'inclusion et d'inclure les en-têtes appropriés dans chaque fichier.
Essayez vous-même
#include <iostream>
#include <string>
#include "Notifier.h"
#include "EmailNotifier.h"
using namespace std;
// TODO: Définir la classe SMSNotifier ici qui hérite publiquement de Notifier
// - Constructeur qui passe recipient à la classe de base
// - Surcharger send() pour afficher : SMS to <recipient>: <message>
// (remplacer complètement le comportement de base, ne pas appeler le send() parent)
int main() {
string recipient;
string message;
getline(cin, recipient);
getline(cin, message);
// TODO: Créer un objet Notifier avec recipient
// Afficher "--- Base Notifier ---"
// Appeler send() avec message
// TODO: Créer un objet EmailNotifier avec recipient
// Afficher "--- Email Notifier ---"
// Appeler send() avec message
// TODO: Créer un objet SMSNotifier avec recipient
// Afficher "--- SMS Notifier ---"
// Appeler send() avec message
return 0;
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation Orientée Objet
1Fondamentaux de la POO
Fichiers externesGénération et compilation en C++Fichiers d’en-tête et fichiers sourceEspaces de noms et portéeIntroduction à la POO en C++Classes et objetsLe pointeur « this »Méthodes (fonctions membres)Attributs (membres de données)Bases des constructeurs et destructeursRécapitulatif - Calculatrice simple4Propriétés de classe
Membres d’instance et statiquesAccesseurs et mutateursFonctions membres constMot-clé mutableMéthodes et variables statiquesFonctions et classes amiesRécapitulatif - Gestionnaire de compte bancaire7Héritage
Héritage de baseNiveaux d’accès de l’héritageOrdre d’appel des constructeurs et destructeursRedéfinition des méthodesFonctions virtuelles et VTableHéritage multipleHéritage virtuelRécapitulatif : hiérarchie des employés10Vue d’ensemble de la STL
Vue d’ensemble et philosophie de la STLConteneurs de la STLItérateursAlgorithmes de la STLFoncteurs et expressions lambdaRécapitulatif - fréquence des mots13Modèles de conception, partie 1
Introduction aux modèles de conceptionModèle SingletonFabrique et fabrique abstraiteModèle BuilderModèle ObserverModèle Strategy2Gestion de la mémoire
Mémoire de pile ou de tasPointeurs et référencesMémoire dynamique (new/delete)Pointeurs intelligents en C++RAII en C++Récapitulatif - Gestionnaire de tableaux dynamiques5Encapsulation
Spécificateurs d’accès en C++Spécificateurs d’accès en profondeurMasquage de l’informationStruct vs classeClasses imbriquées et internesRécapitulatif - Système de gestion des dossiers étudiants8Polymorphisme
Polymorphisme à la compilation vs à l’exécutionSurcharge de fonctionsRetour sur les fonctions virtuellesFonctions virtuelles puresClasses abstraitesConception d’interfaces en C++Conversion dynamique et RTTIRécapitulatif - Calculateur de formes3Constructeurs et destructeurs
Constructeur par défautConstructeur paramétréConstructeur de copieConstructeur de déplacementListes d’initialisation des constructeursConstructeurs déléguésApprofondissement des destructeursRègle des trois / cinq / zéroRécapitulatif - Classe String6Surcharge des opérateurs
Introduction à la surcharge des opérateursSurcharge des opérateurs arithmétiquesSurcharge des opérateurs de comparaisonOpérateurs de fluxSurcharge de l’opérateur d’affectationSurcharge des opérateurs [] et ()Opérateurs de conversion de typeRécapitulatif – Classe Matrix9Templates
Templates de fonctionsTemplates de classesSpécialisation des templatesTemplates variadiquesBases de SFINAE et des traits de typesRécapitulatif - Conteneur génériqueEntraînez-vous par vous-même : Compilateur C++ en ligne