Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Определение производных классов



Каждый производный класс наследует данные и функции-члены своего базового класса, и программировать приходится лишь те аспекты, которые изменяют или расширяют его поведение. К примеру, в классе NameQuery необходимо определить реализацию eval(). Кроме того, нужна поддержка для хранения слова-операнда, представленного объектом класса типа string.

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

typedef vector< location> loc;

 

class NameQuery: public Query {

public:

//...

 

  // переопределяет виртуальную функцию Query:: eval()2

   virtual void eval();

 

// функция чтения

   string name() const { return _name; }

 

static const map< string, loc*> *word_map() { return _word_map; }

 

protected:

   string _name;

static map< string, loc*> *_word_map;

};

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

 

! daddy

 

то операнд запроса NotQuery включает следующий вектор позиций:

 

daddy ((0, 8), (3, 3), (5, 5))

 

Вектор позиций, возвращаемый в ответ на исходный запрос, должен включать все номера колонок в строках (1, 2, 4). Кроме того, он должен включать все номера колонок в строке (0), кроме колонки (8), все номера колонок в строке (3), кроме колонки (3), и все номера колонок в строке (5), кроме колонки (5).

Простейший способ вычислить все это – создать единственный разделяемый всеми объектами вектор позиций, который содержит пары (строка, колонка) для каждого слова в тексте (полную реализацию мы рассмотрим в разделе 17.5, когда будем обсуждать функцию eval() класса NotQuery). Так или иначе, этот член мы объявим статическим для NotQuery.

Вот определение класса NotQuery (и снова рассмотрение конструкторов, деструктора и копирующего оператора присваивания отложено):

class NotQuery: public Query {

public:

//...

 

// альтернативный синтаксис: явно употреблено ключевое слово virtual

// переопределение Query:: eval()

   virtual void eval();

 

// функция доступа для чтения

   const Query *op() const { return _op; }

   static const vector< location > * all_locs() {

                                return _all_locs; }

 

protected:

   Query *_op;

   static const vector< location > *_all_locs;

};

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

class OrQuery: public Query {

public:

//...

 

   virtual void eval();

 

   const Query *rop() const { return _rop; }

   const Query *lop() const { return _lop; }

 

protected:

   Query *_lop;

   Query *_rop;

};

Любой объект AndQuery должен иметь доступ к числу слов в каждой строке. В противном случае при обработке запроса AndQuery мы не сможем найти соседние слова, расположенные в двух смежных строках. Например, если есть запрос:

 

tell & & her & & magical

 

то нужная последовательность находится в третьей и четвертой строках:

 

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

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

 

Векторы позиций, ассоциированные с каждым из трех слов, следующие:

 

her ((0, 7), (1, 5), (2, 12), (4, 11))

magical ((3, 0))

tell ((2, 11), (4, 1), (4, 10))

 

Если функция eval() класса AndQuery “не знает”, сколько слов содержится в строке (2), то она не сможет определить, что слова magical и her соседствуют. Мы создадим единственный экземпляр вектора, разделяемый всеми объектами класса, и объявим его статическим членом. (Реализацию eval() мы детально рассмотрим в разделе 17.5.) Итак, определим AndQuery:

class AndQuery: public Query {

public:

// конструкторы обсуждаются в разделе 17.4

   virtual void eval();

       

   const Query *rop() const { return _rop; }

   const Query *lop() const { return _lop; }

 

   static void max_col( const vector< int > *pcol )

         { if (! _max_col ) _max_col = pcol; }

 

protected:

   Query *_lop;

   Query *_rop;

   static const vector< int > *_max_col;

};

Резюме

Открытый интерфейс каждого из четырех производных классов состоит из их открытых членов и унаследованных открытых членов Query. Когда мы пишем:

Query *pq = new NmaeQuery( " Monet" );

то получить доступ к открытому интерфейсу Query можно только через pq. А если пишем:

pq-> eval();

то вызывается реализация виртуальной eval() из производного класса, на объект которого указывает pq, в данном случае – из класса NameQuery. Строкой

pq-> display();

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

void

Query::

display()

{

if (! _solution-> size() ) {

cout < < " \n\tИзвините, "

      < < " подходящих строк в тексте не найдено.\n"

      < < endl;

}

 

set< short>:: const_iterator

  it = _solution-> begin(),

  end_it = _solution-> end();

 

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

int line = *it;

 

    // не будем пользоваться нумерацией строк с 0...

cout < < " (" < < line+1 < < " ) "

      < < (*_text_file)[line] < < '\n';

}

 

cout < < endl;

}

В этом разделе мы попытались определить иерархию классов Query. Однако вопрос о том, как же построить с ее помощью структуру данных, описывающую запрос пользователя, остался без ответа. Когда мы приступим к реализации, это определение придется пересмотреть и расширить. Но прежде нам предстоит более детально изучить механизм наследования в языке C++.

Упражнение 17.3

Рассмотрите приведенные члены иерархии классов для поддержки библиотеки из упражнения 17.1 (раздел 17.1). Выявите возможные кандидаты на роль виртуальных функций, а также те члены, которые являются общими для всех предметов, выдаваемых библиотекой, и, следовательно, могут быть представлены в базовом классе. (Примечание: LibMember – это абстракция человека, которому разрешено брать из библиотеки различные предметы; Date – класс, представляющий календарную дату.)

class Library {

public:

bool check_out( LibMember* ); // выдать

bool check_in ( LibMember* ); // принять назад

bool is_late( const Date& today ); // просрочил

double apply_fine();            // наложить штраф

ostream& print( ostream& =cout );

 

Date* due_date() const;         // ожидаемая дата возврата

Date* date_borrowed() const;    // дата выдачи

 

string title() const;           // название

const LibMember* member() const; // записавшийся

};

Упражнение 17.4

Идентифицируйте члены базового и производных классов для той иерархии, которую вы выбрали в упражнении 17.2 (раздел 17.1). Задайте виртуальные функции, а также открытые и защищенные члены.

Упражнение 17.5

Какие из следующих объявлений неправильны:

class base {... };

 

(a) class Derived: public Derived {... };

(b) class Derived: Base {... };

(c) class Derived: private Base {... };

(d) class Derived: public Base;

(e) class Derived inherits Base {... };


Поделиться:



Последнее изменение этой страницы: 2019-04-09; Просмотров: 277; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.034 с.)
Главная | Случайная страница | Обратная связь