Mehrfachvererbung
Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy. Lektion 53 von 104.
C++ ermöglicht es einer Klasse, gleichzeitig von mehreren Basisklassen zu erben. Das nennt man Mehrfachvererbung und ermöglicht es dir, Merkmale aus verschiedenen Klassenhierarchien zu kombinieren.
Um von mehreren Klassen zu erben, trenne sie durch Kommas:
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();
}
};Die Klasse MultiFunctionDevice hat jetzt Zugriff auf Methoden von Printer und Scanner:
MultiFunctionDevice mfd;
mfd.print(); // Aus Printer
mfd.scan(); // Aus Scanner
mfd.copy(); // Eigene MethodeKonstruktoren werden in der Reihenfolge aufgerufen, in der die Basisklassen in der Vererbungserklärung aufgeführt sind (von links nach rechts), und Destruktoren in umgekehrter Reihenfolge. Du kannst jedem Basiskonstruktor Argumente in der Initialisierungsliste übergeben:
class MultiFunctionDevice : public Printer, public Scanner {
public:
MultiFunctionDevice() : Printer(), Scanner() {}
};Ein mögliches Problem tritt auf, wenn beide Basisklassen ein Element mit demselben Namen haben. Dadurch entsteht Mehrdeutigkeit, und du musst den Bereichsauflösungsoperator verwenden, um anzugeben, welches Element du meinst:
class A { public: void show() {} };
class B { public: void show() {} };
class C : public A, public B {};
C obj;
obj.A::show(); // Rufe die Version von A auf
obj.B::show(); // Rufe die Version von B aufMehrfachvererbung ist leistungsfähig, kann aber zu komplexen Situationen führen, insbesondere wenn Basisklassen einen gemeinsamen Vorfahren haben. Diese Herausforderung betrachten wir in der nächsten Lektion.
Aufgabe
EinfachErstellen wir ein Smart-Home-Gerätesystem, das mithilfe von Mehrfachvererbung Funktionen aus mehreren Basisklassen kombiniert. Du erstellst Geräte, die sich sowohl mit WiFi verbinden als auch per Sprache steuern lassen, und kombinierst diese Funktionen anschließend in einem einheitlichen Smart Speaker.
Du verteilst deinen Code auf drei Dateien:
WiFiDevice.h: Definiere eine KlasseWiFiDevice, die ein beliebiges Gerät darstellt, das sich mit einem Netzwerk verbinden kann:- Ein geschütztes Mitglied
std::string networkName - Einen Konstruktor, der einen Netzwerknamen entgegennimmt und speichert
- Eine öffentliche Methode
connect(), die Folgendes ausgibt:Connecting to WiFi: <networkName> - Eine öffentliche Methode
status(), die Folgendes ausgibt:WiFi Status: Connected
- Ein geschütztes Mitglied
VoiceControl.h: Definiere eine KlasseVoiceControl, die ein beliebiges Gerät mit Sprachsteuerungsfunktionen darstellt:- Ein geschütztes Mitglied
std::string assistantName - Einen Konstruktor, der einen Namen des Sprachassistenten entgegennimmt und speichert
- Eine öffentliche Methode
listen(), die Folgendes ausgibt:<assistantName> is listening... - Eine öffentliche Methode
status(), die Folgendes ausgibt:Voice Status: Active
- Ein geschütztes Mitglied
main.cpp: Lies drei Eingaben ein (jeweils in einer eigenen Zeile):- Gerätename (Zeichenkette)
- Name des WiFi-Netzwerks (Zeichenkette)
- Name des Sprachassistenten (Zeichenkette)
Definiere eine Klasse
SmartSpeaker, die öffentlich sowohl vonWiFiDeviceals auch vonVoiceControlerbt:- Ein privates Mitglied
std::string deviceName - Einen Konstruktor, der alle drei Parameter entgegennimmt, den Netzwerknamen mithilfe der Initialisierungsliste an
WiFiDeviceund den Namen des Sprachassistenten anVoiceControlübergibt und den Gerätenamen speichert - Eine Methode
powerOn(), die Folgendes ausgibt:<deviceName> powering on..., und danachconnect()undlisten()aufruft - Eine Methode
showAllStatus(), die den Gerätenamen ausgibt und anschließend den Bereichsauflösungsoperator verwendet, um beide Methodenstatus()aufzurufen und so die Mehrdeutigkeit aufzulösen
Erstelle ein Objekt
SmartSpeakermit den Eingabewerten. RufepowerOn()auf, gib eine Leerzeile aus und rufe anschließendshowAllStatus()auf.
Zum Beispiel bei den Eingaben Echo, HomeNetwork und Alexa:
Echo powering on...
Connecting to WiFi: HomeNetwork
Alexa is listening...
Echo Status:
WiFi Status: Connected
Voice Status: ActiveDie Methode showAllStatus() sollte den Gerätenamen gefolgt von " Status:" in einer Zeile ausgeben und anschließend die Methode status() jeder Basisklasse mit WiFiDevice::status() und VoiceControl::status() aufrufen, um Mehrdeutigkeiten zu vermeiden, da beide Basisklassen eine Methode mit demselben Namen haben.
Probier es selbst
#include <iostream>
#include <string>
#include "WiFiDevice.h"
#include "VoiceControl.h"
using namespace std;
// TODO: Definiere die Klasse SmartSpeaker, die öffentlich sowohl von WiFiDevice als auch von VoiceControl erbt
// - Privates Mitglied: deviceName (string)
// - Konstruktor: nimmt deviceName, networkName und assistantName entgegen
// Verwende die Initialisierungsliste, um networkName an WiFiDevice und assistantName an VoiceControl zu übergeben
// - Methode powerOn(): gibt "<deviceName> powering on..." aus und ruft dann connect() und listen() auf
// - Methode showAllStatus(): gibt "<deviceName> Status:" aus und ruft dann beide status()-Methoden auf
// unter Verwendung des Gültigkeitsbereichsoperators (WiFiDevice::status() und VoiceControl::status())
class SmartSpeaker {
// TODO: Implementiere hier die Klasse SmartSpeaker
};
int main() {
string deviceName, networkName, assistantName;
getline(cin, deviceName);
getline(cin, networkName);
getline(cin, assistantName);
// TODO: Erstelle ein SmartSpeaker-Objekt mit den Eingabewerten
// TODO: Rufe powerOn() auf
// TODO: Gib eine Leerzeile aus
// TODO: Rufe showAllStatus() auf
return 0;
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienC++ Build & KompilierungHeader-Dateien & QuelldateienNamespaces & GültigkeitsbereichEinführung in OOP mit C++Klassen vs. ObjekteDer „this“-ZeigerMethoden (Member-Funktionen)Attribute (Datenmember)Grundlagen zu Ctors & DtorsRückblick – Einfacher Taschenrechner4Klassenattribute
Instanz- vs. statische MemberGetter und SetterConst-MemberfunktionenMutable-SchlüsselwortStatische Methoden und VariablenFriend-Funktionen und -KlassenZusammenfassung – Bankkontoverwaltung7Vererbung
Grundlagen der VererbungZugriffsebenen bei der VererbungAufrufreihenfolge von Ctor & DtorÜberschreiben von MethodenVirtuelle Funktionen & VTableMehrfachvererbungVirtuelle VererbungRückblick – Mitarbeiterhierarchie2Speicherverwaltung
Stack- vs. Heap-SpeicherZeiger und ReferenzenDynamischer Speicher (new/delete)Intelligente Zeiger in C++RAII in C++Rückblick – Dynamischer Array-Manager5Kapselung
Zugriffsbezeichner in C++Zugriffsbezeichner im DetailInformationsverbergungStruct vs. KlasseVerschachtelte und innere KlassenWiederholung – Studierendenverwaltungssystem8Polymorphie
Polymorphie zur Compilezeit vs. LaufzeitFunktionsüberladungVirtuelle Funktionen erneut betrachtetRein virtuelle FunktionenAbstrakte KlassenInterface-Design in C++Dynamic Casting & RTTIRückblick – Shape-Calculator3Konstruktoren & Destruktoren
StandardkonstruktorParametrisierter KonstruktorKopierkonstruktorMove-KonstruktorKonstruktor-InitialisierungslistenDelegierende KonstruktorenDestruktor im DetailDrei-/Fünf-/Null-RegelZusammenfassung – String-Klasse6Operatorüberladung
Einführung in die OperatorüberladungArithmetische OperatorüberladungVergleichsoperatorüberladungStream-OperatorenZuweisungsoperatorüberladungÜberladung der Operatoren [] und ()TypkonvertierungsoperatorenWiederholung – Matrix-Klasse9Templates
FunktionstemplatesKlassen-TemplatesTemplate-SpezialisierungVariadische TemplatesGrundlagen zu SFINAE und Type TraitsRückblick – Generischer ContainerÜbe selbstständig: Online-C++-Compiler