Abstract classes and dynamic_cast
So far, Shape had the area 0. But a "shape in general" has no sensible area: you cannot draw a shape that is not a circle, not a square and not anything else.
The word "vehicle" on a road sign works the same way. It covers buses and bicycles, but you cannot ride a "vehicle in general", only something specific. Shape serves the same purpose: it is a common name for real shapes, and a Shape object on its own is not needed.
C++ lets you say this directly:
#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
}Abstract classes
Writing = 0 instead of a body declares a pure virtual function: virtual double area() const = 0;. It has no version of its own, and every derived class has to provide one.
A class with at least one such function is abstract:
- you cannot create an object of it;
- references and pointers to it are allowed, like
const Shape& sabove.
A derived class that overrides all such functions is no longer abstract. This way the compiler does not let you forget area in a new shape: a class without it stays abstract, and its object cannot be created. That is why Square sq; compiles, and the call s.area() through a reference to Shape runs Square::area.
dynamic_cast
Sometimes you need to find out what object a pointer to the base class points to. dynamic_cast<Square*>(p) checks it while the program runs:
- if
ppoints to aSquareor an object of a class derived from it, the result is aSquare*pointer; - otherwise the result is
nullptr.
This check needs Shape to have at least one virtual function; otherwise the code does not compile. Interviewers like to ask what dynamic_cast returns for a pointer when the type does not match. The answer is nullptr, so the result is checked before any field is reached through it.
Square sq;
Shape* p = &sq;
Square* q = dynamic_cast<Square*>(p);
if (q != nullptr) {
std::cout << q->side << '\n'; // 2
}Common mistake
An object of an abstract class is created directly:
struct Shape {
virtual double area() const = 0;
};
int main() {
Shape s; // ✗
}error: variable type 'Shape' is an abstract classShape has no version of area, so a Shape object cannot be created. Create an object of a derived class, such as Square, and work with it through const Shape& or Shape*.
Key points
- A function with
= 0instead of a body is pure virtual, and a derived class has to override it. - An object of an abstract class cannot be created, but references and pointers to it can.
dynamic_cast<T*>(p)returns aT*ornullptrwhile the program runs.dynamic_castworks only when the base class has a virtual function.
Subscribe to take the course
The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.