Наследование и полиморфизм
Интерфейсы: один вызов — разное поведение
После урока ты сможешь
- Описывать интерфейс через чисто виртуальные функции
- Реализовывать его с override
- Хранить разные реализации в vector<unique_ptr<Base>>
Шаг 1 из 6
Один вызов — разное поведение
Приложение должно отправлять уведомления: в консоль, в Telegram, по почте. Код, который решает, когда уведомлять, не должен знать, как именно это происходит. Ему нужно только одно умение: «отправь сообщение».
Такое умение описывается интерфейсом: классом, в котором есть только объявления того, что можно делать. Конкретные классы наследуют интерфейс и реализуют каждый по-своему:
#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(...) выполняет разный код в зависимости от реального типа объекта.