Шрифт:
–основанным или объектно-ориентированным. Он опирается на то, что действия над объектами неоднородных списков выполняются одинаковым образом. Смысл этих действий зависит от фактичекого типа объектов, находящихся в списке (что становится ивестно только на стадии выполнения), а не просто от типа элментов списка (который компилятору известен).
7.6 Законченная программа
Разберем процесс написания программы для рисования на экране геометрических фигур. Она естественным образом раздляется на три части:
1. Администратор экрана: подпрограммы низкого уровня и структуры данных, определяющие экран;он ведает только точками и прямыми линиями,
2. Библиотека фигур: набор определений основных фигур вроде прямоугольника и круга и стандартные программы для работы с ними и
3. Прикладная программа: множество определений, специалзированных для данного приложения, и код, который их использует.
Эти три части скорее всего будут писать разные люди (в разных организациях и в разное время). При этом части будут скорее всего писать именно в указанном порядке с тем осложнющим обстоятельством, что у разработчиков нижнего уровня не будет точного представления, для чего их код в конечном счете будет использоваться. Это отражено в приводимом примере. Чтбы пример был короче, графическая библиотека предоставляет только весьма ограниченный сервис, а сама прикладная програма очень проста. Чтобы читатель смог испытать программу, даже если у него нет совсем никаких графических средств, использется чрезвычайно простая концепция экрана. Не должно соствить труда заменить эту экранную часть программы чем-нибудь подходящим, не изменяя код библиотеки фигур и прикладной программы.
7.6.1 Администратор экрана
Вначале было намерение написать администратор экрана на C (а не на С++), чтобы подчеркнуть разделение уровней реалзации. Это оказалось слишком утомительным, поэтому пришлось пойти на компромисс: используется стиль C (нет функций члнов, виртуальных функций, определяемых пользователем операций и т.п.), однако применяются конструкторы, надлежащим образом описываются и проверяются параметры функций и т.д. Оглядывясь назад, можно сказать, что администратор экрана очень пхож на C программу, которую потом модифицировали, чтобы вопользоваться средствами С++ не переписывая все полностью.
Экран представляется как двумерный массив символов, работу с которым осуществляют функции put_point и put_line, использующие при обращении с экраном структуру point:
// файл screen.h
const XMAX=40, YMAX=24;
struct point (* int x,y; point (**) point(int a, int b) (* x=a; y=b; *) *);
overload put_point; extern void put_point(int a, int b); inline void put_point(point p) (* put_point(p.x,p.y); *)
overload put_line; extern void put_line(int, int, int, int); inline void put_line(point a, point b) (* put_line(a.x,a.y,b.x,b.y); *)
extern void screen_init; extern void screen_refresh; extern void screen_clear;
#include «stream.h»
Перед первым использованием функции put экран надо инциализировать с помощью screen_init, а изменения в структре данных экрана отображаются на экране только после вызова screen_refresh. Как увидит пользователь, это «обновление» («refresh») осуществляется просто посредством печати новой копии экрана под его предыдущим вариантом. Вот функции и оределения данных для экрана:
#include «screen.h» #include «stream.h»
enum color (* black='*', white=' ' *);
char screen[XMAX][YNAX];
void screen_init (* for (int y=0; y«YMAX; y++) for (int x=0; x«XMAX; x++) screen[x][y] = white; *)
Точки печатаются, только если они есть на экране:
inline int on_screen(int a, int b) (* return 0«=a amp; amp; a«XMAX amp; amp; 0«=b amp; amp; b«YMAX; *)
void put_point(int a, int b) (* if (on_screen(a,b)) screen[a][b] = black; *)
Для рисования линий используется функция put_line:
void put_line(int x0, int y0, int x1, int y1) /* Строит линию от (x0,y0) до (x1,y1). Строится линия b(x-x0) + a(y-y0) = 0. Минимизирует abs(eps), где eps = 2*(b(x-x0)+ a(y-y0)). См. Newman and Sproull: ``Principles of Interactive Computer Graphics'' McGraw-Hill, New York, 1979, pp 33-44. */ (* register dx = 1; int a = x1 – x0; if (a « 0) dx = -1, a = -a; register dy = 1; int b = y1 – y0;
if (b « 0) dy = -1, b = -b; int two_a = 2*a; int two_b = 2*b; int xcrit = -b + two_a; register eps = 0; for (;;) (* put_point(x0,y0); if(x0==x1 amp; amp; y0==y1) break; if(eps „= xcrit) x0 += dx, eps += two_b; if(eps“=a !! a«=b) y0 += dy, eps -= two_a; *) *)
Предоставляются функции для очистки экрана и его обноления:
void screen_clear (* screen_init; *) // очистка
void screen_refresh // обновление (* for (int y=YMAX-1; 0«=y; y–) (* // сверху вниз for (int x=0; x«XMAX; x++) // слева направо cout.put(screen[x][y]); cout.put('\n'); *) *)
Функция ostream::put применяется для печати символов как символов; ostream::operator«« печатает символы как млые целые. Пока что вы может представлять себе, что эти опрделения доступны только в откомпилированном виде, который вы изменить не можете.
7.6.2 Библиотека фигур
Нам нужно определить общее понятие фигуры (shape). Это надо сделать таким образом, чтобы оно использовалось (как бзовый класс) всеми конкретными фигурами (например, кругами и квадратами), и так, чтобы любой фигурой можно было манипулровать исключительно через интерфейс, предоставляемый классом shape: