Перейти к содержимому
Теория · Классы и ООП · урок 13 из 15

Абстрактный класс и dynamic_cast

До сих пор у Shape была площадь 0. Но у «фигуры вообще» разумной площади нет: нарисовать фигуру, которая не круг, не квадрат и ничто другое, нельзя.

Так же устроено слово «транспорт» на дорожном знаке. Оно объединяет автобусы и велосипеды, но поехать на «транспорте вообще» нельзя, только на чём-то конкретном. Shape нужен для того же: это общее имя для настоящих фигур, а сам по себе объект Shape не нужен.

C++ позволяет сказать это прямо:

C++
#include <iostream>

struct Shape {
    virtual double area() const = 0;
};
struct Square : public Shape {
    double side = 2;
    double area() const override { return side * side; }
};

int main() {
    Square sq;
    const Shape& s = sq;
    std::cout << s.area() << '\n';  // 4
}

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

Запись = 0 вместо тела объявляет чисто виртуальную функцию: virtual double area() const = 0;. У неё нет своей версии, и каждый производный класс обязан дать свою.

Класс, в котором есть хоть одна такая функция, — абстрактный:

  • создать его объект нельзя;
  • ссылки и указатели на него можно, как const Shape& s выше.

Производный класс, который переопределил все такие функции, уже не абстрактный. Так компилятор не даст забыть area в новой фигуре: класс без неё останется абстрактным, и его объект не создастся. Поэтому Square sq; компилируется, и вызов s.area() через ссылку на Shape выполняет Square::area.

dynamic_cast

Иногда по указателю на базовый класс нужно узнать, на какой объект он указывает. dynamic_cast<Square*>(p) проверяет это во время работы программы:

  • если p указывает на Square или объект производного от него класса, результат — указатель Square*;
  • иначе результат — nullptr.

Такая проверка требует, чтобы у Shape была хотя бы одна виртуальная функция, иначе код не скомпилируется. На собеседовании любят спросить, что вернёт dynamic_cast для указателя, если тип не совпал. Ответ: nullptr, поэтому результат проверяют, прежде чем обращаться через него к полям.

C++
Square sq;
Shape* p = &sq;
Square* q = dynamic_cast<Square*>(p);
if (q != nullptr) {
    std::cout << q->side << '\n';  // 2
}

Частая ошибка

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

C++
struct Shape {
    virtual double area() const = 0;
};

int main() {
    Shape s;  // ✗
}
TEXT
error: variable type 'Shape' is an abstract class

У Shape нет версии area, поэтому объект Shape создать нельзя. Создайте объект производного класса, например Square, и работайте с ним через const Shape& или Shape*.

Главное

  • Функция с = 0 вместо тела — чисто виртуальная, и производный класс обязан её переопределить.
  • Объект абстрактного класса создать нельзя, а ссылки и указатели на него можно.
  • dynamic_cast<T*>(p) во время работы программы возвращает T* или nullptr.
  • dynamic_cast работает, только если у базового класса есть виртуальная функция.

Подпишись, чтобы пройти курс

Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.

Войти другим способом