Shape
Text
, Rectangle
и Circle
лучше “знают”, как нарисовать свои объекты. На самом деле все такие классы это “знают”. Помимо всего прочего, такие классы точно “знают” внутреннее представление информации. Например, объект класса Circle
определяется точкой и радиусом, а не, скажем, отрезком линии. Генерирование требуемых битов для объекта класса Circle
на основе точки и радиуса там, где это необходимо, и тогда, когда это необходимо, не слишком сложная и затратная работа. По этой причине в классе Circle
определяется своя собственная функция draw_lines()
, которую мы хотим вызывать, а не функция draw_lines()
из класса Shape
. Именно это означает слово virtual
в объявлении функции Shape::draw_lines()
.struct Shape {
// ...
virtual void draw_lines() const;
// пусть каждый производный класс
// сам определяет свою собственную функцию draw_lines(),
// если это необходимо
// ...
};
struct Circle : Shape {
// ...
void draw_lines() const; // " замещение " функции
// Shape::draw_lines()
// ...
};
Итак, функция draw_lines()
Shape
должна как-то вызывать одну из функций-членов класса Circle
, если фигурой является объект класса Shape
, и одну из функций-членов класса Rectangle
, если фигура является объектом класса Rectangle
. Вот что означает слово virtual
в объявлении функции draw_lines()
: если класс является производным от класса Shape
, то он должен самостоятельно объявить свою собственную функцию draw_lines()
(с таким же именем, как функция draw_lines()
в классе Shape
), которая будет вызвана вместо функции draw_lines()
из класса. В главе 13 показано, как это сделано в классах Text
, Circle
, Closed_polyline
и т.д. Определение функции в производном классе, используемой с помощью интерфейса базового класса, называют Обратите внимание на то, что, несмотря на свою главную роль в классе Shape
draw_lines()
находится в разделе protected
. Это сделано не для того, чтобы подчеркнуть, что она предназначена для вызова “общим пользователем” — для этого есть функция draw()
. Просто тем самым мы указали, что функция draw_lines()
— это “деталь реализации”, используемая функцией draw()
и классами, производными от класса Shape
.На этом завершается описание нашей графической модели, начатое в разделе 12.2. Система, управляющая экраном, “знает” о классе Window
Window
“знает” о классе Shape
и может вызывать его функцию-член draw()
. В заключение функция draw()
вызывает функцию draw_lines()
, чтобы нарисовать конкретную фигуру. Вызов функции gui_main()
в нашем пользовательском коде запускает драйвер экрана.Что делает функция gui_main()
wait_for_button()
, которая вызывала драйвер экрана более простым способом.Функция move()
Shape
просто перемещает каждую хранимую точку на определенное расстояние относительно текущей позиции.void Shape::move(int dx, int dy) // перемещает фигуру +=dx and +=dy
{
for (int i = 0; i
points[i].x+=dx;
points[i].y+=dy;
}
}
Подобно функции draw_lines()
move()
является виртуальной, поскольку производный класс может иметь данные, которые необходимо переместить и о которых может “не знать” класс Shape
. В качестве примера можно привести класс Axis
(см. разделы 12.7.3 и 15.4).Функция move()
Shape
; мы ввели ее для удобства и в качестве примера еще одной виртуальной функции. Каждый вид фигуры, имеющей точки, не хранящиеся в базовом классе Shape
, должен определить свою собственную функцию move()
. 14.2.4. Копирование и изменчивость
private:
Shape(const Shape&); // prevent copying
Shape& operator=(const Shape&);