Héritage multiple
Fait partie de la section Programmation Orientée Objet du Journey C++ de Coddy — leçon 53 sur 104.
Le C++ permet à une classe d'hériter de plusieurs classes de base simultanément. C'est ce qu'on appelle l'héritage multiple et cela vous permet de combiner des fonctionnalités provenant de différentes hiérarchies de classes.
Pour hériter de plusieurs classes, séparez-les par des virgules :
class Printer {
public:
void print() {
std::cout << "Printing document" << std::endl;
}
};
class Scanner {
public:
void scan() {
std::cout << "Scanning document" << std::endl;
}
};
class MultiFunctionDevice : public Printer, public Scanner {
public:
void copy() {
scan();
print();
}
};La classe MultiFunctionDevice a désormais accès aux méthodes de Printer et de Scanner :
MultiFunctionDevice mfd;
mfd.print(); // Depuis Printer
mfd.scan(); // Depuis Scanner
mfd.copy(); // Sa propre méthodeLes constructeurs sont appelés dans l'ordre où les classes de base sont listées dans la déclaration d'héritage (de gauche à droite), et les destructeurs dans l'ordre inverse. Vous pouvez passer des arguments à chaque constructeur de base dans la liste d'initialisation :
class MultiFunctionDevice : public Printer, public Scanner {
public:
MultiFunctionDevice() : Printer(), Scanner() {}
};Un problème potentiel survient lorsque les deux classes de base possèdent un membre portant le même nom. Cela crée une ambiguïté, et vous devez utiliser l'opérateur de résolution de portée pour spécifier celui que vous visez :
class A { public: void show() {} };
class B { public: void show() {} };
class C : public A, public B {};
C obj;
obj.A::show(); // Appeler la version de A
obj.B::show(); // Appeler la version de BL'héritage multiple est puissant, mais il peut mener à des situations complexes, particulièrement lorsque les classes de base partagent un ancêtre commun. Nous explorerons ce défi dans la prochaine leçon.
Défi
FacileConstruisons un système d'appareils domestiques intelligents qui combine les capacités de plusieurs classes de base en utilisant l'héritage multiple. Vous allez créer des appareils capables à la fois de se connecter au WiFi et d'être contrôlés par la voix, puis combiner ces fonctionnalités dans une enceinte intelligente unifiée.
Vous organiserez votre code sur trois fichiers :
WiFiDevice.h: Définissez une classeWiFiDevicequi représente tout appareil capable de se connecter à un réseau :- Un membre protégé
std::string networkName - Un constructeur qui prend un nom de réseau et le stocke
- Une méthode publique
connect()qui affiche :Connecting to WiFi: <networkName> - Une méthode publique
status()qui affiche :WiFi Status: Connected
- Un membre protégé
VoiceControl.h: Définissez une classeVoiceControlqui représente tout appareil doté de capacités vocales :- Un membre protégé
std::string assistantName - Un constructeur qui prend un nom d'assistant et le stocke
- Une méthode publique
listen()qui affiche :<assistantName> is listening... - Une méthode publique
status()qui affiche :Voice Status: Active
- Un membre protégé
main.cpp: Lisez trois entrées (chacune sur une ligne séparée) :- Nom de l'appareil (chaîne de caractères)
- Nom du réseau WiFi (chaîne de caractères)
- Nom de l'assistant vocal (chaîne de caractères)
Définissez une classe
SmartSpeakerqui hérite publiquement deWiFiDeviceet deVoiceControl:- Un membre privé
std::string deviceName - Un constructeur qui prend les trois paramètres, transmet le nom du réseau à
WiFiDeviceet le nom de l'assistant àVoiceControlen utilisant la liste d'initialisation, et stocke le nom de l'appareil - Une méthode
powerOn()qui affiche :<deviceName> powering on..., puis appelleconnect()etlisten() - Une méthode
showAllStatus()qui affiche le nom de l'appareil, puis utilise l'opérateur de résolution de portée pour appeler les deux méthodesstatus()afin de résoudre l'ambiguïté
Créez un objet
SmartSpeakeravec les valeurs d'entrée. AppelezpowerOn(), affichez une ligne vide, puis appelezshowAllStatus().
Par exemple, avec les entrées Echo, HomeNetwork, et Alexa :
Echo powering on...
Connecting to WiFi: HomeNetwork
Alexa is listening...
Echo Status:
WiFi Status: Connected
Voice Status: ActiveLa méthode showAllStatus() doit afficher le nom de l'appareil suivi de " Status:" sur une ligne, puis appeler la méthode status() de chaque classe de base en utilisant WiFiDevice::status() et VoiceControl::status() pour éviter toute ambiguïté puisque les deux classes de base possèdent une méthode portant le même nom.
Aide-mémoire
Le C++ prend en charge l'héritage multiple, permettant à une classe d'hériter de plusieurs classes de base simultanément :
class DerivedClass : public BaseClass1, public BaseClass2 {
// Corps de la classe
};La classe dérivée a accès aux membres de toutes les classes de base :
class Printer {
public:
void print() {
std::cout << "Printing document" << std::endl;
}
};
class Scanner {
public:
void scan() {
std::cout << "Scanning document" << std::endl;
}
};
class MultiFunctionDevice : public Printer, public Scanner {
public:
void copy() {
scan(); // Depuis Scanner
print(); // Depuis Printer
}
};Les constructeurs des classes de base sont appelés dans l'ordre où ils apparaissent dans la liste d'héritage (de gauche à droite). Utilisez la liste d'initialisation pour transmettre des arguments à chaque constructeur de base :
class MultiFunctionDevice : public Printer, public Scanner {
public:
MultiFunctionDevice() : Printer(), Scanner() {}
};Lorsque plusieurs classes de base possèdent des membres portant le même nom, utilisez l'opérateur de résolution de portée pour lever l'ambiguïté :
class A { public: void show() {} };
class B { public: void show() {} };
class C : public A, public B {};
C obj;
obj.A::show(); // Appelle la version de A
obj.B::show(); // Appelle la version de BEssayez vous-même
#include <iostream>
#include <string>
#include "WiFiDevice.h"
#include "VoiceControl.h"
using namespace std;
// TODO: Définir la classe SmartSpeaker qui hérite publiquement de WiFiDevice et VoiceControl
// - Membre privé : deviceName (string)
// - Constructeur : prend deviceName, networkName, assistantName
// Utiliser la liste d'initialisation pour passer networkName à WiFiDevice et assistantName à VoiceControl
// - Méthode powerOn() : affiche "<deviceName> powering on...", puis appelle connect() et listen()
// - Méthode showAllStatus() : affiche "<deviceName> Status:", puis appelle les deux méthodes status()
// en utilisant la résolution de portée (WiFiDevice::status() et VoiceControl::status())
class SmartSpeaker {
// TODO: Implémenter la classe SmartSpeaker ici
};
int main() {
string deviceName, networkName, assistantName;
getline(cin, deviceName);
getline(cin, networkName);
getline(cin, assistantName);
// TODO: Créer un objet SmartSpeaker avec les valeurs d'entrée
// TODO: Appeler powerOn()
// TODO: Afficher une ligne vide
// TODO: Appeler showAllStatus()
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 externesBuild et Compilation C++Fichiers d'en-tête et Fichiers sourceEspaces de noms et PortéeIntroduction à la POO en C++Classes vs ObjetsLe pointeur 'this'Méthodes (Fonctions membres)Attributs (Membres de données)Bases des Ctors et DtorsRécapitulatif - Calculatrice simple4Propriétés de classe
Membres d'instance vs membres statiquesGetters et SettersFonctions membres constMot-clé mutableMéthodes et variables statiquesFonctions et classes amiesRécapitulatif - Gestionnaire de compte bancaire7Héritage
Héritage de baseNiveaux d'accès à l'héritageOrdre d'appel des Ctor et DtorRedéfinition de méthodesFonctions virtuelles et VTableHéritage multipleHéritage virtuelRécapitulatif - Hiérarchie des employés10Présentation de la STL
Présentation et philosophie de la STLConteneurs de la STLItérateursAlgorithmes de la STLFoncteurs et expressions lambdaRécapitulatif - Fréquence des mots13Patrons de conception, Partie 1
Introduction aux patrons de conceptionPatron SingletonFabrique & Fabrique abstraitePatron MonteurPatron ObservateurPatron Stratégie2Gestion de la mémoire
Mémoire Pile vs TasPointeurs et RéférencesMémoire dynamique (new/delete)Smart Pointers 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 d'informationStruct vs ClassClasses imbriquées et internesRécapitulatif - Système de gestion d'étudiants8Polymorphisme
Polymorphisme : Compilation vs ExécutionSurcharge de fonctionsRetour sur les fonctions virtuellesFonctions virtuelles puresClasses abstraitesConception d'interfaces en C++Dynamic Casting & RTTIRécapitulatif - Calculateur de formes11Concepts avancés de la POO
Composition vs HéritageMixins via CRTPIdiome PimplEffacement de typeEnum Classes & Typage fortGestion des exceptions en POOHiérarchies d'exceptions personnalisées14Patrons de conception, Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Template MethodPatron ÉtatPatron CompositeRAII en tant que patron3Constructeurs et Destructeurs
Constructeur par défautConstructeur paramétréConstructeur de copieConstructeur de déplacementListes d'initialisation du constructeurConstructeurs déléguésAnalyse approfondie du destructeurRègle de trois / cinq / zéroRécapitulatif - Classe String6Surcharge d'opérateurs
Introduction à la surcharge d'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 de templatesTemplates variadiquesBases de SFINAE & Type TraitsRécapitulatif - Conteneur générique