Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
c++book1.doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
2 Мб
Скачать
☆

7.5 Неоднородные Списки

Предыдущие списки были однородными. То есть, в список помещались только объекты одного типа. Это обеспечивалось ап­паратом производных классов. Списки не обязательно должны быть однородными. Список, заданный в виде указателей на класс, может содержать объекты любого класса, производного от этого класса. То есть, список может быть неоднородным. Веро­ятно, это единственный наиболее важный и полезный аспект про­изводных классов, и он весьма существенно используется в сти­ле программирования, который демонстрируется приведенным выше примером. Этот стиль программирования часто называют объектно -основанным или объектно-ориентированным. Он опирается на то, что действия над объектами неоднородных списков выполняются одинаковым образом. Смысл этих действий зависит от фактичес­кого типа объектов, находящихся в списке (что становится из­вестно только на стадии выполнения), а не просто от типа эле­ментов списка (который компилятору известен).

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 && a<XMAX && 0<=b && 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 && 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<<() печатает символы как ма­лые целые. Пока что вы может представлять себе, что эти опре­деления доступны только в откомпилированном виде, который вы изменить не можете.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]