Программирование >>  Инициализация объектов класса, структура 

1 ... 291 292 293 [ 294 ] 295 296 297 ... 395


void print ( Query object,

const Query *pointer, const Query &reference

до момента выполнения невозможно определить, какой экземпляр print() вызывается r->print();

pointer->print(); reference.print();

всегда вызается Query::print() object.print();

int main() {

NameQuery firebird( firebird );

print( firebird, &firebird, firebird );

ссылки, но не сами объекты:

В данном примере оба обращения через указатель pointer и ссылку reference разрешаются своим динамическим типом; в обоих случаях вызывается NameQuery::print() . Обращение же через объект object всегда приводит к вызову Query::print(). (Пример программ:, в которой используется эффект усечения , приведен в разделе 18.6.2.)

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

17.5.1. Виртуальный ввод/вывод

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

ostream& print( ostream &os = cout ) const;

Функцию print() следует объявить виртуальной, поскольку ее реализации зависят от типа, но нам нужно вызывать ее через указатель типа Query*. Например, для класса

ostream&

A\ndQuery::print ( ostream &os ) const {

lop->print( os ); os << && ; rop->print( os );

AndQuery эта функция могла бы выглядеть так:

является объектом NameQuery. Часть nq, принадлежащая NameQuery, усечена перед инициализацией qobject, поскольку она не помещается в область памяти, отведенную под объект Query. Для поддержки этой парадигма: приходится использовать указатели и



class Query public:

virtual ostreams print( ostream &os=cout ) const {}

...

ее как пустую функцию, а потом сделаем чисто виртуальной:

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

ошибка: кочевое слово virtual может только в определении класса

появляться

print() приведет к ошибке компиляции:

virtual ostreams Query::print( ostreams ) const { ... }

Правильный вариант не должен включать слово virtual.

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

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

fiery ss bird shyly

пользователь ищет вхождения пары слов

Pfiery bird

или одного слова

shyly

С другой стороны, запрос

Необходимо объявить print() виртуальной функцией в абстрактном базовом Query, иначе мы не сможем вызвать ее для членов классов AndQury, OrQuery и NotQuery, являющихся указателями на операнды соответствующих запросов типа Query*. Однако для самого Query разумной реализации print() не существует. Поэтому мы определим



fiery hair

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

class Query {

public:

установить lparen и rparen

void lparen( short lp ) { lparen = lp; } void rparen( short rp ) { rparen = rp; }

получить значения lparen и

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

[ fiery && ( bird hair )

найдет все вхождения любой из пар

Pfiery bird



1 ... 291 292 293 [ 294 ] 295 296 297 ... 395

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