Программирование >>  Поддержка объектно-ориентированного программирования 

1 ... 108 109 110 [ 111 ] 112 113 114 ... 120


void caller()

slist iter sli; input iter ii(cin); заполнение sli user(sli); user(ii);

Мы применили конкретный тип для реализации абстрактного типа, но можно использовать его и независимо от абстрактных типов или просто вводить такие типы для повышения эффективности программы, см. также $$1 3.5. Кроме того, можно использовать один конкретный тип для реализации нескольких абстрактных типов.

В разделе $$1 3.9 описывается более гибкий итератор. Для него зависимость от реализации, которая поставляет подлежащие итерации объекты, определяется в момент инициализации и может изменяться в ходе выполнения программы.

13.4 Узловые классы

В действительности иерархия классов строится, исходя из совсем другой концепции производных классов, чем концепция интерфейс-реализация, которая использовалась для абстрактных типов. Класс рассматривается как фундамент строения. Но даже, если в основании находится абстрактный класс, он допускает некоторое представление в программе и сам предоставляет для производных классов какие-то полезные функции. Примерами узловых классов могут служить классы rectangle ($$6.4.2) и satellite ($$6.5.1 ). Обычно в иерархии класс представляет некоторое общее понятие, а производные классы представляют конкретные варианты этого понятия. Узловой класс является неотъемлемой частью иерархии классов. Он пользуется сервисом, представляемым базовыми классами, сам обеспечивает определенный сервис и предоставляет виртуальные функции и (или) защищенный интерфейс, чтобы позволить дальнейшую детализацию своих операций в производных классах.

Типичный узловой класс не только предоставляет реализацию интерфейса, задаваемого его базовым классом (как это делает класс реализации по отношению к абстрактному типу), но и сам расширяет интерфейс, добавляя новые функции. Рассмотрим в качестве примера класс dialog box, который представляет окно некоторого вида на экране. В этом окне появляются вопросы пользователю и в нем он задает свой ответ с помощью нажатия клавиши или мыши :

class dialog box : public window {

public:

dialog box(const char* ...);

заканчивающийся нулем список

slist iter() : current elem(0) { }

class input iter : public iter { isstream& is; public:

T* first(); T* next();

input iter(istream& r) : is(r) { }

Можно таким образом использовать определенные нами типы:

void user(const iter& it)

for (T* p = it.firstO; p; p = it.nextO) { ...



virtual int ask();

Здесь важную роль играет функция ask() и конструктор, с помощью которого программист указывает используемые клавиши и задает их числовые значения. Функция ask() изображает на экране окно и возвращает номер нажатой в ответ клавиши. Можно представить такой вариант использования:

void user()

for (;;) {

какие-то команды dialog box cont( continue ,

try again ,

abort ,

(char*) 0);

switch (cont.askO) {

case 0: return;

case 1: break;

case 2: abort();

Обратим внимание на использование конструктора. Конструктор, как правило, нужен для узлового класса и часто это нетривиальный конструктор. Этим узловые классы отличаются от абстрактных классов, для которых редко нужны конструкторы.

Пользователь класса dialog box ( а не только создатель этого класса) рассчитывает на сервис, представляемый его базовыми классами. В рассматриваемом примере предполагается, что существует некоторое стандартное размещение нового окна на экране. Если пользователь захочет управлять размещением окна, базовый для dialog box класс window (окно) должен предоставлять такую возможность, например:

dialog box cont( continue , try again , abort ,(char*)0); cont.move(some point);

Здесь функция движения окна move() рассчитывает на определенные функции базовых классов.

Сам класс dialog box является хорошим кандидатом для построения производных классов. Например, вполне разумно иметь такое окно, в котором, кроме нажатия клавиши или ввода с мышью, можно задавать строку символов (скажем, имя файла). Такое окно dbox w str строится как производный класс от простого окна dialog box:

class dbox w str : public dialog box {

строка запроса пользователю список обозначений клавиш

public:

dbox w str (

const char* sl, const char* ... );

int ask();

virtual char* get string(); ...

Функция get string() является той операцией, с помощью которой программист получает заданную пользователем строку. Функция ask() из класса dbox w str гарантирует, что строка введена правильно, а если пользователь не стал вводить строку, то тогда в программу возвращается соответствующее значение (0).

void user2()

обозначений клавиш



...

dbox w str file name( please enter file name , done ,

(char*)0);

file name.ask();

char* p = file name.get string();

if (p) {

используем имя файла

else {

имя файла не задано

Подведем итог - узловой класс должен:

[1 ] рассчитывать на свои базовые классы как для их реализации, так и для представления сервиса пользователям этих классов;

[2] представлять более полный интерфейс (т.е. интерфейс с большим числом функций-членов) пользователям, чем базовые классы;

[3] основывать в первую очередь (но не исключительно) свой общий интерфейс на виртуальных функциях;

[4] зависеть от всех своих (прямых и косвенных) базовых классов;

[5] иметь смысл только в контексте своих базовых классов;

[6] служить базовым классом для построения производных классов;

[7] воплощаться в объекте.

Не все, но многие, узловые классы будут удовлетворять условиям 1 , 2, 6 и 7. Класс, который не удовлетворяет условию 6, походит на конкретный тип и может быть назван конкретным узловым классом. Класс, который не удовлетворяет условию 7, походит на абстрактный тип и может быть назван абстрактным узловым классом. У многих узловых классов есть защищенные члены, чтобы предоставить для производных классов менее ограниченный интерфейс.

Укажем на следствие условия 4: для трансляции своей программы пользователь узлового класса должен включить описания всех его прямых и косвенных базовых классов, а также описания всех тех классов, от которых, в свою очередь, зависят базовые классы. В этом узловой класс опять представляет контраст с абстрактнгм типом. Пользователь абстрактного типа не зависит от всех классов, использующихся для реализации типа и для трансляции своей программы не должен включать их описания.

13.5 Динамическая информация о типе

Иногда бывает полезно знать истинный тип объекта до его использования в каких-либо операциях. Рассмотрим функцию my(set&) из $$13.3.

void my set(set& s)

for ( T* p = s.firstO; p; p = s.nextO) {

мой код

...

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



1 ... 108 109 110 [ 111 ] 112 113 114 ... 120

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика