Упражнение 15.18. С учетом классов Base и производных от него, и типов объектов, приведенных в комментариях, укажите, какие из следующих присвоений допустимы. Объясните, почему некорректны недопустимые.

Base *p = &d1 // d1 имеет тип Pub_Derv

p = &d2       // d2 имеет тип Priv_Derv

p = &d3       // d3 имеет тип Prot_Derv

p = &dd1      // dd1 имеет тип Derived_from_Public

p = &dd2      // dd2 имеет тип Derived_from_Private

p = &dd3      // dd3 имеет тип Derived_from_Protected

Упражнение 15.19. Предположим, у каждого из классов: Base и производных от него, есть функция-член в формате

void memfcn(Base &b) { b = *this; }

Укажите, была ли эта функция допустима для каждого класса.

Упражнение 15.20. Напишите код проверки ответов на предыдущие два упражнения.

Упражнение 15.21. Выберите одну из следующих общих абстракций, содержащих семейство типов (или любую собственную). Организуйте типы в иерархию наследования.

(a) Форматы графических файлов (например: gif, tiff, jpeg, bmp)

(b) Геометрические примитивы (например: box, circle, sphere, cone)

(c) Типы языка С++ (например: class, function, member function)

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

<p><image l:href="#reader.png"/>15.6. Область видимости класса при наследовании</p>

Каждый класс определяет собственную область видимости (scope) (см. раздел 7.4), в рамках которой определены его члены. При наследовании область видимости производного класса (см. раздел 2.2.4) вкладывается в области видимости его базовых классов. Если имя не найдено в области видимости производного класса, поиск его определения продолжается в областях видимости базовых классов.

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

Bulk_quote bulk;

cout << bulk.isbn();

В этом коде поиск определения имени isbn() осуществляется следующим образом.

• Поскольку вызывается функция isbn() объекта типа Bulk_quote, поиск начинается в классе Bulk_quote. В этом классе имя isbn() не найдено.

• Поскольку класс Bulk_quote происходит от класса Disc_quote, в нем и продолжается поиск. Имя все еще не найдено.

• Поскольку класс Disc_quote происходит от класса Quote, поиск продолжается в нем. В этом классе находится определение имени isbn(); таким образом, вызов isbn() распознается как вызов функции isbn() класса Quote.

Поиск имен осуществляется во время компиляции

Статический тип (см. раздел 15.2.3) объекта, ссылки или указателя определяет, какие члены этого объекта будут видимы. Даже когда статический и динамический типы отличаются (это бывает в случае, когда используется ссылка или указатель на базовый класс), именно статический тип определяет применимые члены. Например, в класс Disc_quote можно было бы добавить функцию-член, которая возвращает пару (тип pair) (см. раздел 11.2.3), содержащую минимальное (или максимальное) количество и цену со скидкой.

class Disc_quote : public Quote {

public:

 std::pair discount_policy() const

 { return {quantity, discount}; }

 // другие члены как прежде

};

Функцию discount_policy() можно использовать только через объект, указатель, или ссылку на тип Disc_quote, или класс, производный от него:

Bulk_quote bulk;

Bulk_quote *bulkP = &bulk // статический и динамический типы совпадают

Quote *itemP = &bulk // статический и динамический типы отличаются

bulkP->discount_policy();  // ok: bulkP имеет тип Bulk_quote*

itemP->discount_policy();  // ошибка: itemP имеет тип Quote*

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

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