Абстрактные классы
Часть раздела Объектно-ориентированное программирование путешествия по C++ на Coddy. Урок 60 из 104.
abstract class — это любой class, который содержит как минимум одну чистую виртуальную function. Поскольку у него неполный интерфейс, вы cannot создавать объекты abstract class напрямую — он существует исключительно для того, чтобы от него можно было унаследоваться (inherited).
class Shape {
public:
virtual double area() = 0; // Чисто виртуальная — делает Shape абстрактным
virtual double perimeter() = 0; // Ещё одна чисто виртуальная функция
void printInfo() { // Обычный метод — имеет реализацию
std::cout << "Area: " << area() << std::endl;
}
virtual ~Shape() = default;
};
Shape s; // Ошибка: нельзя создать экземпляр абстрактного классаКлассы abstract могут содержать сочетание чистых виртуальных functions (без implementation), обычных виртуальных functions (с implementation по умолчанию) и невиртуальных functions. Это позволяет define общее поведение, одновременно требуя от классов Derived реализовать определённую функциональность.
class Circle : public Shape {
double radius;
public:
Circle(double r) : radius(r) {}
double area() override { return 3.14159 * radius * radius; }
double perimeter() override { return 2 * 3.14159 * radius; }
};
Circle c(5.0); // OK: Circle реализует все чисто виртуальные функции
c.printInfo(); // Использует унаследованный printInfo(), вызывает area() у CircleПроизводный класс должен реализовать all чистые виртуальные функции, чтобы стать конкретным (доступным для создания экземпляров). Если он оставляет какие-либо функции нереализованными, он сам остаётся abstract. Этот механизм гарантирует, что каждый конкретный класс в вашей иерархии предоставляет требуемую функциональность.
Задание
ЛегкоДавайте создадим систему медиаплеера, демонстрирующую, как abstract классы определяют контракты для разных типов медиаданных. Вы создадите abstract базовый класс, устанавливающий, что должен делать каждый медиаплеер, а затем Implement конкретные плееры для аудио- и видеоконтента.
Вы организуете код в трёх файлах:
MediaPlayer.h: Declare abstractMediaPlayerclass, который служит шаблоном для всех типов медиаданных. Ваш abstract class должен содержать:- защищённый член
std::string title - защищённый член
int duration(в секундах) - constructor, инициализирующий оба значения
- чистый виртуальный метод
play(): каждый тип медиаданных будет воспроизводиться по-своему - чистый виртуальный метод
getMediaType(), возвращающийstd::string - обычный (не виртуальный) метод
showInfo(), который выводит:<title> (<duration>s) - Type: <mediaType>. Этот метод должен вызыватьgetMediaType(), чтобы получить тип - виртуальный destructor
- защищённый член
Players.h: Implement два конкретных медиаплеера, которые наследуются отMediaPlayer:AudioPlayer:- закрытый член
std::string artist - constructor, принимающий title, duration и artist
- Implement
play()для вывода:Playing audio: <title> by <artist> - Implement
getMediaType()для возврата"Audio"
VideoPlayer:- закрытый член
std::string resolution - constructor, принимающий title, duration и resolution
- Implement
play()для вывода:Playing video: <title> in <resolution> - Implement
getMediaType()для возврата"Video"
- закрытый член
main.cpp: Считайте четыре входных значения (каждое в отдельной строке):- название аудиотрека
- длительность аудиозаписи (целое число)
- название видео
- длительность видео (целое число)
Create
AudioPlayerс artist"Unknown Artist"иVideoPlayerс resolution"1080p". Сохраните оба объекта в array указателейMediaPlayer*.Пройдите в цикле по array и для каждого плеера сначала вызовите
showInfo(), а затемplay(). Выводите пустую строку между каждым медиаматериалом. После завершения освободите dynamically выделенные объекты.
Например, для входных данных Summer Vibes, 180, Nature Documentary и 3600:
Summer Vibes (180s) - Type: Audio
Playing audio: Summer Vibes by Unknown Artist
Nature Documentary (3600s) - Type: Video
Playing video: Nature Documentary in 1080pОбратите внимание, как метод showInfo() определён один раз в abstract базовом class, но вызывает чистый виртуальный getMediaType(). Это демонстрирует, как abstract классы могут сочетать конкретные реализации с методами, которые производные классы должны предоставлять. Не забудьте использовать ключевое слово override для всех переопределённых методов.
Попробуйте сами
#include <iostream>
#include <string>
#include "MediaPlayer.h"
#include "Players.h"
int main() {
// Считать входные данные
std::string audioTitle;
int audioDuration;
std::string videoTitle;
int videoDuration;
std::getline(std::cin, audioTitle);
std::cin >> audioDuration;
std::cin.ignore();
std::getline(std::cin, videoTitle);
std::cin >> videoDuration;
// TODO: Создать AudioPlayer с артистом "Unknown Artist"
// TODO: Создать VideoPlayer с разрешением "1080p"
// TODO: Сохранить оба в массиве указателей MediaPlayer*
// TODO: Пройти по массиву и для каждого плеера:
// 1. Вызвать showInfo()
// 2. Вызвать play()
// 3. Вывести пустую строку между каждым медиаэлементом
// TODO: Очистить динамически выделенные объекты
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++