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

}

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 { ... };

17.3. Доступ к членам базового класса

Объект производного класса фактически построен из нескольких частей. Каждый базовый класс вносит свою долю в виде подобъекта, составленного из нестатических данных-членов этого класса. Объект производного класса построен из подобъектов, соответствующих каждому из его базовых, а также из части, включающей нестатические члены самого производного класса. Так, наш объект NameQuery состоит из подобъекта Query, содержащего члены _loc и _solution, и части, принадлежащей NameQuery, - она содержит только член _name.

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

void

NameQuery::

display_partial_solution( ostream &os )

{

os _name

" is found in "

(_solution ? _solution-size() : 0)

" lines of text\n";

}

Это касается и доступа к унаследованным функциям-членам базового класса: мы вызываем их так, как если бы они были членами производного - либо через его объект:

NameQuery nq( "Frost" );

// вызывается NameQuery::eval()

nq.eval();

// вызывается Query::display()

nq.display();

либо непосредственно из тела другой (или той же самой) функции-члена:

void

NameQuery::

match_count()

{

if ( ! _solution )

// вызывается Query::_vec2set()

_solution = _vec2set( &_loc );

return _solution-size();

}

Однако прямой доступ из производного класса к членам базового запрещен, если имя последнего скрыто в производном классе:

class Diffident {

public: // ...

protected:

int _mumble;

// ...

};

class Shy : public Diffident {

public: // ...

protected:

// имя Diffident::_mumble скрыто

string _mumble;

// ...

};

В области видимости Shy употребление неквалифицированного имени _mumble разрешается в пользу члена _mumble класса Shy (объекта string), даже если такое использование в данном контексте недопустимо:

void

Shy::

turn_eyes_down()

{

// ...

_mumble = "excuse me"; // правильно

// ошибка: int Diffident::_mumble скрыто

_mumble = -1;

}

Некоторые компиляторы помечают это как ошибку типизации. Для доступа к члену базового класса, имя которого скрыто в производном, необходимо квалифицировать имя члена базового класса именем самого этого класса с помощью оператора разрешения области видимости. Так выглядит правильная реализация функции-члена turn_eyes_down():