Абстрактный класс и dynamic_cast
До сих пор у Shape была площадь 0. Но у «фигуры вообще» разумной площади нет: нарисовать фигуру, которая не круг, не квадрат и ничто другое, нельзя.
Так же устроено слово «транспорт» на дорожном знаке. Оно объединяет автобусы и велосипеды, но поехать на «транспорте вообще» нельзя, только на чём-то конкретном. Shape нужен для того же: это общее имя для настоящих фигур, а сам по себе объект Shape не нужен.
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, поэтому результат проверяют, прежде чем обращаться через него к полям.
Square sq;
Shape* p = &sq;
Square* q = dynamic_cast<Square*>(p);
if (q != nullptr) {
std::cout << q->side << '\n'; // 2
}Частая ошибка
Объект абстрактного класса пытаются создать напрямую:
struct Shape {
virtual double area() const = 0;
};
int main() {
Shape s; // ✗
}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 и подпишись на канал — откроются все уроки.