Абстрактные классы

Как выглядит животное?
- Уже успели ответить? На самом деле, ответа на этот вопрос нет, так как для ответа требуется уточнить, какое именно животное. Получается, что понятие "животное" - абстрактное.

Как выглядит геометрическая фигура? - Уже успели ответить? Я думаю, что нет, ведь здесь то же требуется уточнение, о какой именно геометрической фигуре идёт речь. Получается, что понятие "геометрическая фигура" - абстрактное.

Примеров обстрактных понятий можно приводить много...

Язык программирования C++ поддерживает объектно-ориентированный подход к разработке приложений. А придумана парадигма ООП была для того, что-бы программисты могли в коде описать реальные сущности.
Но как же быть с абстрактными понятиями?
- На этот случай, в ООП придумали абстрактные классы. От них нельзя создать объект, зато от них можно наследоваться.

Абстрактный класс - это класс, который содержит хотя бы одну чистую виртуальную функцию и не может быть использован для создания объектов напрямую.

или

Абстрактные классы — это классы, которые не могут быть инстанцированы непосредственно и могут содержать абстрактные методы (методы без реализации) и обычные методы (с реализацией). Они служат основой для других классов, предоставляя общий интерфейс и функциональность для их расширения(наследования).


Чистые виртуальные функции

Чистая виртуальная функция объявляется с помощью = 0 в конце объявления:

class Animal { protected: std::string nickname; int age; std::string breed; public: Animal(std::string name) : nickname(name) {} std::string getNickname() const { return nickname; } // Чистая виртуальная функция - делает класс абстрактным virtual void sound() const = 0; // Обычный метод с реализацией void run() const { std::cout << nickname << " побежал(а)" << std::endl; } // Виртуальный деструктор (важно для корректного удаления объектов) virtual ~Animal() {} };
Пояснение:
  • sound() - чистая виртуальная функция, не имеет реализации в базовом классе

  • Класс, содержащий хотя бы одну чистую виртуальную функцию, становится абстрактным

  • Можно создавать обычные методы с реализацией (как run())

  • Виртуальный деструктор необходим для корректного удаления объектов через указатель на базовый класс

Для создания конкретного класса, который будет наследовать абстрактный класс, необходимо реализовать его абстрактные (virtual) методы.


Наследование от абстрактного класса

Класс Cat (наследует Animal):
class Cat : public Animal { public: Cat(std::string name) : Animal(name) {} // Обязательная реализация чистой виртуальной функции void sound() const override { std::cout << "Мяу!" << std::endl; } };
Класс Dog (наследует Animal):
class Dog : public Animal { public: Dog(std::string name) : Animal(name) {} // Обязательная реализация чистой виртуальной функции void sound() const override { std::cout << "Гав!" << std::endl; } };
Пояснение:
  • Производные классы должны реализовать все чистые виртуальные функции

  • Ключевое слово override явно указывает на переопределение метода

  • Если не реализовать все чистые виртуальные функции, класс останется абстрактным


Использование абстрактных классов

int main() { // Работа через указатели на базовый класс Animal* dog = new Dog("Бобик"); dog->sound(); // Вывод: Гав! dog->run(); // Вывод: Бобик побежал(а) Animal* cat = new Cat("Мурзик"); cat->sound(); // Вывод: Мяу! cat->run(); // Вывод: Мурзик побежал(а) // Ошибка компиляции - нельзя создать объект абстрактного класса // Animal animal("Name"); delete dog; delete cat; return 0; }
Пояснение:
  • Можно создавать указатели и ссылки на абстрактный класс

  • Нельзя создавать объекты абстрактного класса напрямую

  • Полиморфные вызовы работают корректно благодаря виртуальным функциям

  • Важно не забывать освобождать память (или использовать умные указатели)


Практический пример: геометрические фигуры

#include <iostream> #include #include class Shape { public: // Чистая виртуальная функция для расчета площади virtual double area() const = 0; // Чистая виртуальная функция для отображения информации virtual void printInfo() const = 0; virtual ~Shape() {} }; class Circle : public Shape { double radius; public: Circle(double r) : radius(r) {} double area() const override { return 3.14159 * radius * radius; } void printInfo() const override { std::cout << "Круг с радиусом " << radius << ", площадь: " << area() << std::endl; } }; class Rectangle : public Shape { double width, height; public: Rectangle(double w, double h) : width(w), height(h) {} double area() const override { return width * height; } void printInfo() const override { std::cout << "Прямоугольник " << width << "x" << height << ", площадь: " << area() << std::endl; } }; int main() { std::vector<std::unique_ptr<Shape>> shapes; shapes.push_back(std::make_unique<Circle>(5.0)); shapes.push_back(std::make_unique<Rectangle>(4.0, 6.0)); for (const auto& shape : shapes) { shape->printInfo(); } return 0; }
Разбор примера
  1. Shape - абстрактный класс с двумя чистыми виртуальными функциями

  2. Circle и Rectangle реализуют специфичные версии этих методов

  3. Используется полиморфизм через указатели на базовый класс

  4. Применены умные указатели для автоматического управления памятью

  5. Каждая фигура реализует свою версию расчета площади и вывода информации


Практический пример: Музыкальные инструменты и исполнители

Суть задания

Задание демонстрирует принципы полиморфизма в C++ через моделирование взаимодействия музыкантов с различными музыкальными инструментами. Основная цель - показать, как:

  1. Создается иерархия классов музыкальных инструментов

  2. Реализуется полиморфное поведение через виртуальные функции

  3. Класс-исполнитель может работать с любым инструментом через общий интерфейс

  4. Достигается гибкость и расширяемость кода

Базовый класс Instrument
class Instrument { public: virtual void MakeSound() = 0; };
  • Содержит чисто виртуальную функцию MakeSound()

  • Является абстрактным классом (нельзя создать объект)

  • Определяет общий интерфейс для всех музыкальных инструментов

Конкретные классы инструментов
class Accordion : public Instrument { public: void MakeSound() override { cout << "Accordion is playing\n"; } }; class Piano : public Instrument { public: void MakeSound() override { cout << "Piano is playing\n"; } };
  • Наследуют от базового класса Instrument

  • Реализуют свою версию метода MakeSound()

  • Каждый инструмент имеет уникальное звучание

Класс Performer
class Accordion : public Instrument { public: void MakeSound() override { cout << "Accordion is playing\n"; } }; class Piano : public Instrument { public: void MakeSound() override { cout << "Piano is playing\n"; } };
  • Содержит указатель на Instrument (абстрактный класс)

  • Может работать с ЛЮБЫМ инструментом, наследующим от Instrument

  • Метод play() вызывает полиморфный метод MakeSound()

Работа в main()
int main() { Instrument* accordion = new Accordion(); Instrument* piano = new Piano(); Performer performer1(accordion); Performer performer2(piano); performer1.play(); // Вызов Accordion::MakeSound() performer2.play(); // Вызов Piano::MakeSound() delete accordion; delete piano; return 0; }
  1. Создаются конкретные инструменты через указатели на базовый класс

  2. Создаются исполнители, которым передаются инструменты

  3. При вызове play() происходит полиморфный вызов нужной реализации MakeSound()

  4. В конце освобождается выделенная память

Полный код:
#include <iostream> #include <vector> using namespace std; // Базовый класс инструмента class Instrument { public: virtual void MakeSound() = 0; }; // Класс аккордеона, наследующий от базового класса class Accordion : public Instrument { public: void MakeSound() override { cout << "Accordion is playing\n"; } }; // Класс пианино, также наследующий от базового класса class Piano : public Instrument { public: void MakeSound() override { cout << "Piano is playing\n"; } }; // Класс исполнителя (Performer) class Performer { private: Instrument* instrument; // Указатель на объект инструмента public: // Конструктор, принимающий указатель на объект инструмента Performer(Instrument* inst) : instrument(inst) {} // Метод play(), который вызывает метод MakeSound() у объекта инструмента void play() { if (instrument != nullptr) { instrument->MakeSound(); // Вызываем метод MakeSound() у объекта инструмента } else { cout << "No instrument assigned to the performer.\n"; } } }; int main() { // Создаем объекты инструментов Instrument* accordion = new Accordion(); Instrument* piano = new Piano(); // Создаем исполнителей и передаем им инструменты Performer performer1(accordion); Performer performer2(piano); // Исполнители играют на инструментах performer1.play(); performer2.play(); // Освобождаем память delete accordion; delete piano; return 0; }

Зачем использовать абстрактные классы?

  • Упрощение структуры кода: Позволяет создать общую базу, от которой будут наследоваться дочерние классы.
  • Полиморфизм: - Позволяет обращаться к объектам разных классов через один интерфейс.

Абстрактные классы являются мощным инструментом в Java для построения архитектуры программ, которая подчеркивает принципы наследования и полиморфизма, способствуя созданию более чистого и понятного кода.


Комментарии

Добавить комментарий

Чтобы оставить комменатрий необходимо Авторизоваться