Объекты, помещаемые в olist, теряют свой тип. Это ознчает, что компилятор знает только то, что они olink'и. Првильный тип можно восстановить с помощью явного преобразовния типа объектов, вынутых из olist. Например:
void f() (* olist ll; name nn; ll.insert( amp;nn); // тип amp;nn потерян name* pn = (name*)ll.get(); // и восстановлен *)
Другой способ: тип можно восстановить, выводя еще один класс из olist для обработки преобразования типа:
class onlist : public olist (* // ... name* get() (* return (name*)olist::get(); *) *);
Имя name может одновременно находиться только в одном olist. Для имен это, может быть, и не подходит, но в классах, для которых это подойдет полностью, недостатка нет. Например, класс фигур shape использует для поддержки списка всех фигур именно этот метод. Обратите внимание, что можно было бы опрделить slist как производный от olist, объединяя таким обрзом оба понятия. Однако использование базовых и производных классов на таком микроскопическом уровне может очень сильно исказить код.
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») осуществляется просто посредством печати новой копии экрана под его предыдущим вариантом. Вот функции и оределения данных для экрана: