Выбрать главу

class Query {

public:

// ...

// установить _lparen и _rparen

void lparen( short lp ) { _lparen = lp; }

void rparen( short rp ) { _rparen = rp; }

// получить значения_lparen и _rparen

short lparen() { return _lparen; }

short rparen() { return _rparen; }

// напечатать левую и правую скобки

void print_lparen( short cnt, ostream& os ) const;

void print_rparen( short cnt, ostream& os ) const;

protected:

// счетчики левых и правых скобок

short _lparen;

short _rparen;

// ...

};

_lparen - это количество левых, а _rparen - правых скобок, которое должно быть выведено при распечатке объекта. (В разделе 17.7 мы покажем, как вычисляются такие величины и как происходит присваивание обоим членам.) Вот пример обработки запроса с большим числом скобок:

== ( untamed || ( fiery || ( shyly ) ) )

evaluate word: untamed

_lparen: 1

_rparen: 0

evaluate Or

_lparen: 0

_rparen: 0

evaluate word: fiery

_lparen: 1

_rparen: 0

evaluate 0r

_lparen: 0

_rparen: 0

evaluate word: shyly

_lparen: 1

_rparen: 0

evaluate right parens:

_rparen: 3

( untamed ( 1 ) lines match

( fiery ( 1 ) lines match

( shyly ( 1 ) lines match

( fiery || (shyly ( 2 ) lines match3

( untamed || ( fiery || ( shyly ))) ( 3 ) lines match

Requested query: ( untamed || ( fiery || ( shyly ) ) )

( 3 ) like a fiery bird in flight. A beautiful fiery bird, he tells her,

( 4 ) magical but untamed. "Daddy, shush, there is no such thing,"

( 6 ) Shyly, she asks, "I mean, Daddy, is there?"

Реализация print() для класса NameQuery:

ostream&

NameQuery::

print( ostream &os ) const

{

if ( _lparen )

print_lparen( _lparen, os );

os

А так выглядит объявление:

class NameQuery : public Query {

public:

virtual ostream& print( ostream &os ) const;

// ...

};

Чтобы реализация виртуальной функции в производном классе замещала реализацию из базового, прототипы функций обязаны совпадать. Например, если бы мы опустили слово const или объявили еще один параметр, то реализация print() в NameQuery не заместила бы реализацию из базового класса. Возвращаемые значения также должны быть одинаковыми за одним исключением: значение, возвращенное реализацией в производном классе, может принадлежать к типу класса, который открыто наследует классу значения, возвращаемого реализацией в базовом классе. Если бы реализация из базового класса возвращала значение типа Query*, то реализация из производного могла бы возвращать NameQuery*. (Позже при работе с функцией clone() мы покажем, зачем это нужно.) Вот объявление и реализация print() в NotQuery:

class NotQuery : public Query {

public:

virtual ostream& print( ostream &os ) const;

// ...

};

ostream&

NotQuery::

print( ostream &os ) const

{

os " ! ";

if ( _lparen )

print_lparen( _lparen, os );

_op-print( os );

if ( _rparen )

print_rparen( _rparen, os );

return os;

}

Разумеется, вызов print() через _op - виртуальный.

Объявления и реализации этой функции в классах AndQuery и OrQuery практически дублируют друг друга. Поэтому приведем их только для AndQuery:

class AndQuery : public Query {

public:

virtual ostream& print( ostream &os ) const;

// ...

};

ostream&

AndQuery::

print( ostream &os ) const

{

if ( _lparen )

print_lparen( _lparen, os );

_lop-print( os );

os " && ";

_rop-print( os );

if ( _rparen )

print_rparen( _rparen, os );

return os;

}

Такая реализация виртуальной функции print() позволяет вывести любой подтип Query в поток класса ostream или любого другого, производного от него:

cout " Был сформулирован запрос " ;

Query *pq = retrieveQuery();

pq-print( cout );

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

Query *pq = retrieveQuery();

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

*pq

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

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