Множественное наследование
Часть раздела Объектно-ориентированное программирование путешествия по C++ на Coddy. Урок 53 из 104.
C++ позволяет классу одновременно наследовать от нескольких базовых классов. Это называется множественным наследованием и позволяет объединять возможности из разных иерархий классов.
Чтобы наследоваться от нескольких классов, разделите их запятыми:
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();
}
};Классу MultiFunctionDevice теперь доступны методы как из Printer, так и из Scanner:
MultiFunctionDevice mfd;
mfd.print(); // Из Printer
mfd.scan(); // Из Scanner
mfd.copy(); // Его собственный методКонструкторы вызываются в порядке перечисления базовых классов в объявлении наследования (слева направо), а деструкторы — в обратном порядке. Каждому конструктору базового класса можно передать аргументы в списке инициализации:
class MultiFunctionDevice : public Printer, public Scanner {
public:
MultiFunctionDevice() : Printer(), Scanner() {}
};Возможна проблема, если у обоих базовых классов есть член с одинаковым именем. Это создаёт неоднозначность, и необходимо использовать оператор разрешения области видимости, чтобы указать, какой именно член имеется в виду:
class A { public: void show() {} };
class B { public: void show() {} };
class C : public A, public B {};
C obj;
obj.A::show(); // Вызвать версию A
obj.B::show(); // Вызвать версию BМножественное наследование — мощный инструмент, но оно может привести к сложным ситуациям, особенно когда у базовых классов есть общий предок. Мы рассмотрим эту проблему в следующем уроке.
Задание
ЛегкоДавайте создадим систему устройств для умного дома, объединяющую возможности нескольких базовых классов с помощью множественного наследования. Вы создадите устройства, которые могут подключаться к WiFi и управляться голосом, а затем объедините эти функции в единой умной колонке.
Вы распределите код по трём файлам:
WiFiDevice.h: определите классWiFiDevice, представляющий любое устройство, способное подключаться к сети:- Защищённый член
std::string networkName - Конструктор, который принимает имя сети и сохраняет его
- Открытый метод
connect(), который выводит:Connecting to WiFi: <networkName> - Открытый метод
status(), который выводит:WiFi Status: Connected
- Защищённый член
VoiceControl.h: определите классVoiceControl, представляющий любое устройство с голосовым управлением:- Защищённый член
std::string assistantName - Конструктор, который принимает имя голосового помощника и сохраняет его
- Открытый метод
listen(), который выводит:<assistantName> is listening... - Открытый метод
status(), который выводит:Voice Status: Active
- Защищённый член
main.cpp: считайте три входных значения (каждое с новой строки):- Имя устройства (строка)
- Имя сети WiFi (строка)
- Имя голосового помощника (строка)
Определите класс
SmartSpeaker, который открыто наследуется от обоих классов —WiFiDeviceиVoiceControl:- Закрытый член
std::string deviceName - Конструктор, который принимает все три параметра, передаёт имя сети в
WiFiDevice, а имя голосового помощника — вVoiceControlс помощью списка инициализации и сохраняет имя устройства - Метод
powerOn(), который выводит:<deviceName> powering on..., а затем вызываетconnect()иlisten() - Метод
showAllStatus(), который выводит имя устройства, а затем использует оператор разрешения области видимости для вызова обоих методовstatus(), устраняя неоднозначность
Создайте объект
SmartSpeakerс входными значениями. ВызовитеpowerOn(), выведите пустую строку, а затем вызовитеshowAllStatus().
Например, для входных значений Echo, HomeNetwork и Alexa:
Echo powering on...
Connecting to WiFi: HomeNetwork
Alexa is listening...
Echo Status:
WiFi Status: Connected
Voice Status: ActiveМетод showAllStatus() должен вывести имя устройства, за которым следует " Status:" в одной строке, а затем вызвать метод status() каждого базового класса с помощью WiFiDevice::status() и VoiceControl::status(), чтобы избежать неоднозначности, поскольку в обоих базовых классах есть метод с одинаковым именем.
Попробуйте сами
#include <iostream>
#include <string>
#include "WiFiDevice.h"
#include "VoiceControl.h"
using namespace std;
// TODO: Определить класс SmartSpeaker, который публично наследуется от WiFiDevice и VoiceControl
// - Закрытое поле: deviceName (string)
// - Конструктор: принимает deviceName, networkName, assistantName
// Использовать список инициализации, чтобы передать networkName в WiFiDevice, а assistantName — в VoiceControl
// - Метод powerOn(): выводит "<deviceName> powering on...", затем вызывает connect() и listen()
// - Метод showAllStatus(): выводит "<deviceName> Status:", затем вызывает оба метода status()
// с использованием оператора разрешения области видимости (WiFiDevice::status() и VoiceControl::status())
class SmartSpeaker {
// TODO: Реализовать здесь класс SmartSpeaker
};
int main() {
string deviceName, networkName, assistantName;
getline(cin, deviceName);
getline(cin, networkName);
getline(cin, assistantName);
// TODO: Создать объект SmartSpeaker со значениями, полученными на входе
// TODO: Вызвать powerOn()
// TODO: Вывести пустую строку
// TODO: Вызвать showAllStatus()
return 0;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыСборка и компиляция C++Заголовочные файлы и файлы исходного кодаПространства имен и область видимостиВведение в ООП на C++Классы и объектыУказатель 'this'Методы (функции-члены)Атрибуты (члены данных)Основы конструкторов и деструкторовИтоги — Простой калькулятор4Свойства классов
Члены экземпляра против статическихГеттеры и сеттерыКонстантные функции-членыКлючевое слово mutableСтатические методы и переменныеДружественные функции и классыИтоги — Менеджер банковских счетов7Наследование
Основы наследованияУровни доступа при наследованииПорядок вызова конструкторов и деструкторовПереопределение методовВиртуальные функции и VTableМножественное наследованиеВиртуальное наследованиеИтоги — Иерархия сотрудников10Обзор STL
Обзор и философия STLКонтейнеры STLИтераторыАлгоритмы STLФункторы и лямбда-выраженияИтоги: частота слов13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonFactory и Abstract FactoryПаттерн BuilderПаттерн ObserverПаттерн Strategy2Управление памятью
Стек и кучаУказатели и ссылкиДинамическая память (new/delete)Умные указатели в C++RAII в C++Итоги — Менеджер динамического массива5Инкапсуляция
Спецификаторы доступа в C++Спецификаторы доступа: подробный разборСокрытие данныхStruct против ClassВложенные и внутренние классыИтоги — Система учета студентов8Полиморфизм
Полиморфизм компиляции и времени выполненияПерегрузка функцийВиртуальные функции: повторениеЧистые виртуальные функцииАбстрактные классыПроектирование интерфейсов на C++Dynamic Casting и RTTIИтоги: Калькулятор фигур11Продвинутые концепции ООП
Композиция против наследованияМиксины через CRTPИдиома PimplСтирание типовEnum Classes и строгая типизацияОбработка исключений в ООППользовательские иерархии исключений14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикRAII как паттерн3Конструкторы и деструкторы
Конструктор по умолчаниюПараметризованный конструкторКонструктор копированияКонструктор перемещенияСписки инициализации конструктораДелегирующие конструкторыГлубокое погружение в деструкторыПравило трех / пяти / нуляПовторение — класс String6Перегрузка операторов
Введение в перегрузку операторовПерегрузка арифметических операторовПерегрузка операторов сравненияОператоры потоковПерегрузка оператора присваиванияПерегрузка операторов [] и ()Операторы преобразования типовИтоги — класс Matrix9Шаблоны
Шаблоны функцийШаблоны классовСпециализация шаблоновВариативные шаблоныОсновы SFINAE и Type TraitsИтоги — Обобщенный контейнерПотренируйтесь самостоятельно: Онлайн-компилятор C++