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


Иногда возникает необходимость определить класс, который не предполагает создаия конкретных объектов. Например, класс фигуры. В реальности есть конкретные фигуры: квадрат, прямоугольник, треугольник, круг и так далее. Однако абстрактной фигуры самой по себе не существует. В то же время может потребоваться определить для всех фигур какой-то общий класс, который будет содержать общую для всех функциональность. И для описания подобных сущностей используются абстрактные классы.

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

Что такое чистые виртуальные функции (pure virtual functions)? Это функции, которые не имеют определения. Чтобы определить виртуальную функцию как чистую, ее объявление завершается значением "=0". Например, определим абстрактный класс, который представляет геометрическую фигуру:

1
2
3
4
5
6
7
class Figure
{
public:
virtual double getSquare() = 0;
virtual double getPerimeter() = 0;
virtual void showFigureType() = 0;
};
Класс Figure является абтрактным, потому он содержит как минимум одну чистую виртуальную функцию. А в данном случае даже три таких функции. И ни одна из функций не имеет никакой реализации. Реализацию должны определять классы-наследники.

При этом мы не можем создать объект абстрактного класса:

1
Figure figure;
Определим следующую программу:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <iostream>

class Figure
{
public:
virtual double getSquare() =0;
virtual double getPerimeter() =0;
virtual void showFigureType()=0;
};
class Rectangle : public Figure
{
private:
double width;
double height;
public:
Rectangle(double w, double h) : width(w), height(h)
{
}
double getSquare() override
{
return width * height;
}
double getPerimeter() override
{
return width * 2 + height * 2;
}
void showFigureType()
{
std::cout << "Rectangle" << std::endl;
}
};
class Circle : public Figure
{
private:
double radius;
public:
Circle(double r) : radius(r)
{
}
double getSquare() override
{
return radius * radius * 3.14;
}
double getPerimeter() override
{
return 2 * 3.14 * radius;
}
void showFigureType()
{
std::cout << "Circle" << std::endl;
}
};

int main()
{
Rectangle rect(30, 50);
Circle circle(30);

std::cout << "Rectangle square: " << rect.getSquare() << std::endl;
std::cout << "Circle square: " << circle.getSquare() << std::endl;

return 0;
}
Здесь определены два класса-наследника от абстрактного класса Figure - Rectangle (прямоугольник) и Circle (круг). При создании классов-наследников все они должны либо определить для чстых виртуальных функций конкретную реализацию, либо повторить объявление чистой виртуальной функции. Во втором случае производные классы также будут абстрактными.

В данном же случае и Circle, и Rectangle являются конкретными классами и реализуют все виртуальные функции.

Консольный вывод программы:

Rectangle square: 1500
Circle square: 2826
Стоит отметить, что абстрактный класс может определять и обычные функции и переменные, может иметь несколько конструкторов, но при этом нельзя создавать объекты этого абстрактного класса.