Menu
CoddyTech

Множественное наследование

Часть раздела Объектно-ориентированное программирование путешествия по 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

Множественное наследование — мощный инструмент, но оно может привести к сложным ситуациям, особенно когда у базовых классов есть общий предок. Мы рассмотрим эту проблему в следующем уроке.

challenge icon

Задание

Легко

Давайте создадим систему устройств для умного дома, объединяющую возможности нескольких базовых классов с помощью множественного наследования. Вы создадите устройства, которые могут подключаться к 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: считайте три входных значения (каждое с новой строки):
    1. Имя устройства (строка)
    2. Имя сети WiFi (строка)
    3. Имя голосового помощника (строка)

    Определите класс 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;
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор C++