Wielokrotne dziedziczenie
Część sekcji Programowanie obiektowe ścieżki C++ w Coddy. Lekcja 53 z 104.
C++ umożliwia klasie jednoczesne dziedziczenie po wielu klasach bazowych. Nazywa się to dziedziczeniem wielokrotnym i pozwala łączyć funkcje z różnych hierarchii klas.
Aby dziedziczyć po wielu klasach, oddziel je przecinkami:
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();
}
};Klasa MultiFunctionDevice ma teraz dostęp do metod zarówno klasy Printer, jak i klasy Scanner:
MultiFunctionDevice mfd;
mfd.print(); // Z klasy Printer
mfd.scan(); // Z klasy Scanner
mfd.copy(); // Własna metodaKonstruktory są wywoływane w kolejności, w jakiej klasy bazowe są wymienione w deklaracji dziedziczenia (od lewej do prawej), a destruktory w odwrotnej kolejności. Możesz przekazać argumenty do każdego konstruktora klasy bazowej na liście inicjalizacyjnej:
class MultiFunctionDevice : public Printer, public Scanner {
public:
MultiFunctionDevice() : Printer(), Scanner() {}
};Potencjalny problem pojawia się, gdy obie klasy bazowe mają składową o tej samej nazwie. Powoduje to niejednoznaczność, dlatego musisz użyć operatora rozstrzygania zakresu, aby określić, o którą z nich chodzi:
class A { public: void show() {} };
class B { public: void show() {} };
class C : public A, public B {};
C obj;
obj.A::show(); // Wywołaj wersję A
obj.B::show(); // Wywołaj wersję BDziedziczenie wielokrotne jest potężne, ale może prowadzić do złożonych sytuacji, zwłaszcza gdy klasy bazowe mają wspólnego przodka. Przyjrzymy się temu wyzwaniu w następnej lekcji.
Wyzwanie
ŁatwyZbudujmy system urządzeń inteligentnego domu, który łączy możliwości wielu klas bazowych za pomocą wielokrotnego dziedziczenia. Utworzysz urządzenia, które mogą zarówno łączyć się z WiFi, jak i być sterowane głosowo, a następnie połączysz te funkcje w jednym inteligentnym głośniku.
Rozmieścisz kod w trzech plikach:
WiFiDevice.h: Zdefiniuj klasęWiFiDevice, która reprezentuje dowolne urządzenie mogące łączyć się z siecią:- Pole chronione
std::string networkName - Konstruktor, który przyjmuje nazwę sieci i ją zapisuje
- Publiczna metoda
connect(), która wypisuje:Connecting to WiFi: <networkName> - Publiczna metoda
status(), która wypisuje:WiFi Status: Connected
- Pole chronione
VoiceControl.h: Zdefiniuj klasęVoiceControl, która reprezentuje dowolne urządzenie z funkcjami sterowania głosowego:- Pole chronione
std::string assistantName - Konstruktor, który przyjmuje nazwę asystenta i ją zapisuje
- Publiczna metoda
listen(), która wypisuje:<assistantName> is listening... - Publiczna metoda
status(), która wypisuje:Voice Status: Active
- Pole chronione
main.cpp: Wczytaj trzy dane wejściowe (każdą w osobnym wierszu):- Nazwę urządzenia (ciąg znaków)
- Nazwę sieci WiFi (ciąg znaków)
- Nazwę asystenta głosowego (ciąg znaków)
Zdefiniuj klasę
SmartSpeaker, która publicznie dziedziczy zarówno poWiFiDevice, jak i poVoiceControl:- Prywatne pole
std::string deviceName - Konstruktor, który przyjmuje wszystkie trzy parametry, przekazuje nazwę sieci do
WiFiDevicei nazwę asystenta doVoiceControlza pomocą listy inicjalizacyjnej oraz zapisuje nazwę urządzenia - Metoda
powerOn(), która wypisuje:<deviceName> powering on..., a następnie wywołujeconnect()ilisten() - Metoda
showAllStatus(), która wypisuje nazwę urządzenia, a następnie używa operatora zakresu, aby wywołać obie metodystatus()i rozwiązać niejednoznaczność
Utwórz obiekt
SmartSpeakerz użyciem wczytanych wartości. WywołajpowerOn(), wypisz pusty wiersz, a następnie wywołajshowAllStatus().
Na przykład dla danych wejściowych Echo, HomeNetwork i Alexa:
Echo powering on...
Connecting to WiFi: HomeNetwork
Alexa is listening...
Echo Status:
WiFi Status: Connected
Voice Status: ActiveMetoda showAllStatus() powinna wypisać nazwę urządzenia, po której w tym samym wierszu następuje " Status:", a następnie wywołać metodę status() każdej klasy bazowej za pomocą WiFiDevice::status() i VoiceControl::status(), aby uniknąć niejednoznaczności, ponieważ obie klasy bazowe mają metodę o tej samej nazwie.
Spróbuj swoich sił
#include <iostream>
#include <string>
#include "WiFiDevice.h"
#include "VoiceControl.h"
using namespace std;
// TODO: Zdefiniuj klasę SmartSpeaker, która dziedziczy publicznie zarówno po WiFiDevice, jak i VoiceControl
// - Prywatne pole: deviceName (string)
// - Konstruktor: przyjmuje deviceName, networkName, assistantName
// Użyj listy inicjalizacyjnej, aby przekazać networkName do WiFiDevice, a assistantName do VoiceControl
// - Metoda powerOn(): wypisuje "<deviceName> powering on...", a następnie wywołuje connect() i listen()
// - Metoda showAllStatus(): wypisuje "<deviceName> Status:", a następnie wywołuje obie metody status()
// z użyciem operatora zasięgu (WiFiDevice::status() i VoiceControl::status())
class SmartSpeaker {
// TODO: Zaimplementuj tutaj klasę SmartSpeaker
};
int main() {
string deviceName, networkName, assistantName;
getline(cin, deviceName);
getline(cin, networkName);
getline(cin, assistantName);
// TODO: Utwórz obiekt SmartSpeaker z wartościami wejściowymi
// TODO: Wywołaj powerOn()
// TODO: Wypisz pustą linię
// TODO: Wywołaj showAllStatus()
return 0;
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrzneBudowanie i kompilacja C++Pliki nagłówkowe i pliki źródłowePrzestrzenie nazw i zakresWprowadzenie do programowania obiektowego w C++Klasy a obiektyWskaźnik „this”Metody (funkcje składowe)Atrybuty (składowe danych)Podstawy konstruktorów i destruktorówPowtórzenie – prosty kalkulator4Właściwości klas
Elementy instancji a elementy statyczneGettery i setteryStałe funkcje składoweSłowo kluczowe mutableMetody i zmienne statyczneFunkcje i klasy zaprzyjaźnionePodsumowanie – menedżer konta bankowego7Dziedziczenie
Podstawy dziedziczeniaPoziomy dostępu w dziedziczeniuKolejność wywołań konstruktora i destruktoraNadpisywanie metodFunkcje wirtualne i VTableWielokrotne dziedziczenieDziedziczenie wirtualnePowtórzenie — hierarchia pracowników2Zarządzanie pamięcią
Pamięć stosu a stertyWskaźniki i referencjePamięć dynamiczna (new/delete)Inteligentne wskaźniki w C++RAII w C++Podsumowanie — menedżer tablicy dynamicznej5Hermetyzacja
Specyfikatory dostępu w C++Specyfikatory dostępu — szczegółowoUkrywanie informacjiStruktura a klasaKlasy zagnieżdżone i wewnętrznePodsumowanie — system ewidencji studentów8Polimorfizm
Polimorfizm czasu kompilacji i wykonaniaPrzeciążanie funkcjiFunkcje wirtualne — powtórkaCzysto wirtualne funkcjeKlasy abstrakcyjneProjektowanie interfejsów w C++Rzutowanie dynamiczne i RTTIPodsumowanie — kalkulator kształtów3Konstruktory i destruktory
Konstruktor domyślnyKonstruktor z parametramiKonstruktor kopiującyKonstruktor przenoszącyListy inicjalizacyjne konstruktoraKonstruktory delegująceDestruktor — szczegółowe omówienieZasada trzech / pięciu / zeraPodsumowanie — klasa String6Przeciążanie operatorów
Wprowadzenie do przeciążania operatorówPrzeciążanie operatorów arytmetycznychPrzeciążanie operatorów porównaniaOperatory strumieniowePrzeciążanie operatora przypisaniaPrzeciążanie operatorów [] i ()Operatory konwersji typówPodsumowanie — klasa Matrix9Szablony
Szablony funkcjiSzablony klasSpecjalizacja szablonówSzablony wariadycznePodstawy SFINAE i cech typówPodsumowanie — kontener generycznyPoćwicz samodzielnie: Kompilator C++ online