Menu
Coddy logo textTech

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

Часть раздела Объектно-ориентированное программирование путешествия по 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, который представляет любое устройство, способное подключаться к сети:
    • Защищенный (protected) член std::string networkName
    • Конструктор, который принимает имя сети и сохраняет его
    • Публичный метод connect(), который выводит: Connecting to WiFi: <networkName>
    • Публичный метод status(), который выводит: WiFi Status: Connected
  • VoiceControl.h: Определите класс VoiceControl, который представляет любое устройство с голосовыми возможностями:
    • Защищенный (protected) член 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(), чтобы избежать неоднозначности, так как оба базовых класса имеют метод с одинаковым именем.

Шпаргалка

C++ поддерживает множественное наследование, позволяя классу наследоваться от нескольких базовых классов одновременно:

class DerivedClass : public BaseClass1, public BaseClass2 {
    // Тело класса
};

Производный класс имеет доступ к членам всех базовых классов:

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();   // Из Scanner
        print();  // Из Printer
    }
};

Конструкторы базовых классов вызываются в том порядке, в котором они указаны в списке наследования (слева направо). Используйте список инициализации для передачи аргументов каждому базовому конструктору:

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

Попробуйте сами

#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Проверьте себя

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

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