C++
Шрифт:
Использование вывода класса гарантирует, что все частные случаи обобщенного класса разделяют код. Этот метод можно применять только для создания классов объектов того же размра или меньше, чем базовый класс, который используется в маросе. gslist применяется в #7.6.2.
7.3.6 Ограниченные интерфейсы
Класс slist – довольно общего характера. Иногда подобная общность не требуется или даже нежелательна. Ограниченные вды списков, такие как стеки и очереди, даже более обычны, чем сам обобщенный список. Такие структуры данных можно задать, не описав базовый класс как открытый. Например,
#include «slist.h»
class iqueue : slist (* //предполагается sizeof(int)«=sizeof(void*)
public: void put(int a) (* slist::append((void*)a); *) int det (* return int(slist::get); *) iqueue (**) *);
При таком выводе осуществляются два логически разделеных действия: понятие списка ограничивается понятием очереди (сводится к нему), и задается тип int, чтобы свести понятие очереди к типу данных очередь целых, iqueue. Эти два действия можно выполнять и раздельно. Здесь первая часть – это список, ограниченный так, что он может использоваться только как стек:
#include «slist.h»
class stack : slist (* public: slist::insert; slist::get; stack (**) stack(ent a) : (a) (**) *);
который потом используется для создания типа «стек укзателей на символы»:
#include «stack.h»
class cp : stack (* public: void push(char* a) (* slist::insert(a); *) char* pop (* return (char*)slist::get; *) nlist (**) *);
7.4 Добавление к классу
В предыдущих примерах производный класс ничего не добалял к базовому классу. Для производного класса функции опрделялись только чтобы обеспечить преобразование типа. Каждый производный класс просто задавал альтернативный интерфейс к общему множеству программ. Этот специальный случай важен, но наиболее обычная причина определения новых классов как проиводных классов в том, что кто-то хочет иметь то, что предотавляет базовый класс, плюс еще чуть-чуть.
Для производного класса можно определить данные и фунции дополнительно к тем, которые наследуются из его базового класса. Это дает альтернативную стратегию того, как обеспчить средства связанного списка. Заметьте, когда в тот slist, который определялся выше, помещается элемент, то создается slink, содержащий два указателя. На их создание тратится врмя, а ведь без одного из указателей можно обойтись, при услвии, что нужно только чтобы объект мог находиться в одном списке. Так что указатель next на следующий можно поместить в сам объект, вместо того, чтобы помещать его в отдельный обект slink. Идея состоит в том, чтобы создать класс olink с единственным полем next, и класс olist, который может обрабтывать указатели на такие звенья olink. Тогда olist сможет манипулировать объектами любого класса, производного от olink. Буква "o" в названиях стоит для того, чтобы напоминать вам, что объект может находиться одновременно только в одном списке olist:
struct olink (* olink* next;
*);
Класс olist очень напоминает класс slist. Отличие состит в том, что пользователь класса olist манипулирует объектми класса olink непосредственно:
class olist (* olink* last; public: void insert(olink* p); void append(olink* p); olink* get; // ... *);
Мы можем вывести из класса olink класс name:
class name : public 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. Прикладная программа: множество определений, специалзированных для данного приложения, и код, который их использует.
Эти три части скорее всего будут писать разные люди (в разных организациях и в разное время). При этом части будут скорее всего писать именно в указанном порядке с тем осложнющим обстоятельством, что у разработчиков нижнего уровня не будет точного представления, для чего их код в конечном счете будет использоваться. Это отражено в приводимом примере. Чтбы пример был короче, графическая библиотека предоставляет только весьма ограниченный сервис, а сама прикладная програма очень проста. Чтобы читатель смог испытать программу, даже если у него нет совсем никаких графических средств, использется чрезвычайно простая концепция экрана. Не должно соствить труда заменить эту экранную часть программы чем-нибудь подходящим, не изменяя код библиотеки фигур и прикладной программы.