Объекты · Классы и память

Наследование и полиморфизм

Интерфейсы: один вызов — разное поведение

~35 мин 6 шагов

После урока ты сможешь

  • Описывать интерфейс через чисто виртуальные функции
  • Реализовывать его с override
  • Хранить разные реализации в vector<unique_ptr<Base>>

Шаг 1 из 6

Один вызов — разное поведение

Приложение должно отправлять уведомления: в консоль, в Telegram, по почте. Код, который решает, когда уведомлять, не должен знать, как именно это происходит. Ему нужно только одно умение: «отправь сообщение».

Такое умение описывается интерфейсом: классом, в котором есть только объявления того, что можно делать. Конкретные классы наследуют интерфейс и реализуют каждый по-своему:

main.cpp
#include <memory>
#include <print>
#include <string>
#include <utility>
#include <vector>
​
class Notifier {
public:
    virtual ~Notifier() = default;
    virtual void send(const std::string& message) = 0;
};
​
class ConsoleNotifier : public Notifier {
public:
    void send(const std::string& message) override {
        std::println("[консоль] {}", message);
    }
};
​
class EmailNotifier : public Notifier {
public:
    explicit EmailNotifier(std::string address) : address_(std::move(address)) {}
    void send(const std::string& message) override {
        std::println("[письмо для {}] {}", address_, message);
    }
​
private:
    std::string address_;
};
​
void notifyAll(const std::vector<std::unique_ptr<Notifier>>& channels, const std::string& message) {
    for (const auto& channel : channels) {
        channel->send(message);    // какой send — решается во время работы
    }
}
​
int main() {
    std::vector<std::unique_ptr<Notifier>> channels;
    channels.push_back(std::make_unique<ConsoleNotifier>());
    channels.push_back(std::make_unique<EmailNotifier>("ops@example.com"));
​
    notifyAll(channels, "сервер перезагружен");
}

notifyAll ничего не знает о консоли и почте. Добавишь TelegramNotifier — эта функция не изменится ни на строчку. Это и есть полиморфизм: один и тот же вызов channel->send(...) выполняет разный код в зависимости от реального типа объекта.