pq-print( cout );

Однако такой возможности недостаточно. Еще нужно уметь распечатывать любой производный от Query тип, который уже есть или может появиться в будущем, с помощью оператора вывода из библиотеки iostream:

Query *pq = retrieveQuery();

cout " В ответ на запрос "

*pq

" получены следующие результаты:\n" ;

Мы не можем непосредственно предоставить виртуальный оператор вывода, поскольку они являются членами класса ostream. Вместо этого мы должны написать косвенную виртуальную функцию:

inline ostream&

operator ( ostream &os, const Query &q )

{

// виртуальный вызов print()

return q.print( os );

}

Строки

AndQuery query;

// сформулировать запрос ...

cout query endl;

вызывают наш оператор вывода в ostream, который в свою очередь вызывает

q.print( os )

где q привязано к объекту query класса AndQuery, а os - к cout. Если бы вместо этого мы написали:

NameQuery query2( " Salinger" );

cout query2 endl;

то была бы вызвана реализация print() из класса NameQuery. Обращение

Query *pquery = retrieveQuery();

cout *pquery endl;

приводит к вызову той функции print(), которая ассоциирована с объектом, адресуемым указателем pquery в данной точке выполнения программы.

17.5.2. Чисто виртуальные функции

С точки зрения кодирования основная задача, стоящая перед нами в связи с поддержкой пользовательских запросов, - это реализация зависимых от типа операций для каждого из возможных операторов. Для этого мы определили четыре конкретных типа классов: AndQuery, OrQuery и т.д. Однако с точки зрения проектирования наша цель - инкапсулировать обработку каждого вида запроса, спрятать за не зависящим от типа интерфейсом. Это позволит построить ядро приложения, которое не потребует изменений при добавлении или удалении типов.

Чтобы добиться этого, определим абстрактный тип класса Query. При этом мы не будем программировать разные типы пользовательских запросов, а лишь абстрактные операции, применимые к ним:

void doit_and_bedone( vector Query* *pvec )

{

vectorQuery*::iterator

it = pvec-begin(),

end_it = pvec-end();

for ( ; it != end_it; ++it )

{

Query *pq = *it;

cout "обрабатывается " *pq endl;

pq-eval();

pq-display();

delete pq;

}

}

Такое определение позволяет добавлять неограниченное число типов запросов без необходимости изменять или даже перекомпилировать ядро системы, но при условии, что открытый интерфейс нашего абстрактного базового класса Query достаточен для поддержки новых запросов.

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

Поскольку Query - абстрактный класс, объекты которого в приложении не создаются, то никакой разумной реализации виртуальных функций в нем самом мы предложить не можем. Это лишь названия, которые должны быть замещены в производных классах. Напрямую вызывать их мы не будем.

Язык обладает синтаксической конструкцией, обозначающей, что некоторая виртуальная функция предоставляет интерфейс, который должен быть замещен в производных подтипах, но вызываться непосредственно не может. Это чисто виртуальные функции. Объявляются они следующим образом:

class Query {

public:

// объявляется чисто виртуальная функция

virtual ostream& print( ostream&=cout ) const = 0;

// ...

};

Заметьте, что за объявлением функции следует присваивание нуля.

Класс, содержащий (или наследующий) одну или несколько таких функций, распознается компилятором как абстрактный базовый класс. Попытка создать независимый объект абстрактного класса приводит к ошибке компиляции. (Ошибкой является также вызов чисто виртуальной функции с помощью механизма виртуализации.) Например:

// В классе Query объявлены одна или несколько виртуальных функций,

// поэтому программист не может создавать независимые объекты

// класса Query

// правильно: подобъект Query в составе NameQuery

Перейти на страницу:

Похожие книги