Абстрактный класс - это класс, для которого один или несколько методов объявлены, но не определены, это означает, что компилятор знает, что эти методы являются частью класса, но не то, какой код должен выполняться для этого метода. Это так называемые абстрактные методы. Вот пример абстрактного класса.
class shape {
public:
virtual void draw() = 0;
};
Это объявляет абстрактный класс, который указывает, что любые потомки класса должны реализовывать метод draw, если класс должен быть конкретным. Вы не можете создать экземпляр этого класса, потому что он абстрактный, в конце концов, компилятор не будет знать, какой код выполнять, если вы вызвали член draw. Таким образом, вы не можете сделать следующее:
shape my_shape();
my_shape.draw();
Чтобы реально использовать метод draw, вам нужно извлечь классы из этого абстрактного класса, который реализует метод draw, делая классы конкретными:
class circle : public shape {
public:
circle(int x, int y, int radius) {
/* set up the circle */
}
virtual draw() {
/* do stuff to draw the circle */
}
};
class rectangle : public shape {
public:
rectangle(int min_x, int min_y, int max_x, int max_y) {
/* set up rectangle */
}
virtual draw() {
/* do stuff to draw the rectangle */
}
};
Теперь вы можете создать экземпляр конкретных объектов круга и прямоугольника и использовать их методы рисования:
circle my_circle(40, 30, 10);
rectangle my_rectangle(20, 10, 50, 15);
my_circle.draw();
my_rectangle.draw();
Теперь, конечно, вопрос в том, зачем вам это делать? Не могли бы вы с таким же успехом определить классы круга и прямоугольника и покончить со всем классом фигур? Вы можете, но тогда вы не сможете воспользоваться их наследством:
std::vector<shape*> my_scene;
my_scene.push_back(new circle(40, 30, 10));
my_scene.push_back(new rectangle(20, 10, 50, 15));
std::for_each(my_scene.begin(), my_scene.end(), std::mem_fun_ref(&shape::draw)
Этот код позволяет вам собрать все ваши фигуры в один контейнер. Это делает это намного проще, если в вашей сцене много фигур и много разных фигур. Например, теперь мы можем нарисовать все фигуры за один раз, и код, который делает это, даже не должен знать о различных типах фигур, которые у нас есть.
Теперь, наконец, нам нужно знать, почему функция рисования формы является абстрактной, а не просто пустой функцией, т. Е. Почему мы просто не определили:
class shape {
public:
virtual void draw() {
/* do nothing */
}
};
Причина этого в том, что мы на самом деле не хотим, чтобы объекты имели форму типа, они все равно не были бы реальными вещами, они были бы абстрактными. Поэтому нет смысла определять реализацию для метода draw, даже пустую. Делая класс формы абстрактным, мы не можем ошибочно создать экземпляр класса формы или ошибочно вызвать пустую функцию рисования базового класса вместо функции рисования производных классов. По сути, мы определяем интерфейс для любого класса, который хотел бы вести себя как фигуру, мы говорим, что любой такой класс должен иметь метод draw, который выглядит так, как мы и указали.
Чтобы ответить на ваш последний вопрос, не существует такого понятия, как «обычный производный класс», каждый класс является абстрактным или конкретным. Класс, который имеет какие-либо абстрактные методы, является абстрактным, любой класс, который не является конкретным. Это просто способ разграничить два типа классов. Базовый класс может быть либо абстрактным, либо конкретным, а производный класс может быть абстрактным или конкретным:
class abstract_base {
public:
virtual void abstract_method1() = 0;
virtual void abstract_method2() = 0;
};
class concrete_base {
public:
void concrete_method1() {
/* do something */
}
};
class abstract_derived1 : public abstract_base {
public:
virtual void abstract_method3() = 0;
};
class abstract_derived2 : public concrete_base {
public:
virtual void abstract_method3() = 0;
};
class abstract_derived3 : public abstract_base {
public:
virtual abstract_method1() {
/* do something */
}
/* note that we do not provide an implementation for
abstract_method2 so the class is still abstract */
};
class concrete_derived1 : public concrete_base {
public:
void concrete_method2() {
/* do something */
}
};
class concrete_derived2 : public abstract_base {
public:
virtual void abstract_method1() {
/* do something */
}
virtual void abstract_method2() {
/* do something */
}
/* This class is now concrete because no abstract methods remain */
};