Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
inf_otvety_24-40.docx
Скачиваний:
71
Добавлен:
11.05.2015
Размер:
136.35 Кб
Скачать

Внутреннее и защищенное наследование

До сих пор мы использовали только внешнее наследование. Однако в языке Си++ имеется также внутреннее и защищенное наследование. Если перед именем базового класса ставится ключевое слово private, то наследование называется внутренним.

class B : private A

{

. . .

};

В случае внутреннего наследования внешняя и защищенная части базового класса становятся внутренней частью производного класса. Внутренняя часть базового класса остается для производного класса недоступной.

Если перед именем базового класса поставить ключевое слово protected, то будет использоваться защищенное наследование. При нем внешняя и защищенная части базового класса становятся защищенной частью производного класса. Внутренняя часть базового класса остается недоступной для производного класса.

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

Если в классе A был определен какой-то метод:

class A

{

public:

int foo();

};

то запись

B b;

b.foo();

недопустима, так же, как и наследуемый от B

class C

{

int m() {

foo();

}

};

если класс B внутренне наследует A. Если же класс B использовал защищенное наследование, то первая запись b.foo() также была бы неправильной, но зато вторая была бы верна.

Виртуальное наследование

Базовый класс можно объявить виртуальным базовым классом, используя запись:

class Student : virtual Person

{

};

class Librarian : virtual Person

{

};

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

Рис. 10.4.  Структура объекта StudentLibrarian при виртуальном множественном наследовании.

31. Множественное наследование.

Если класс A является базовым классом для B, то B наследует атрибуты A. т. е. B содержит A плюс еще что-то. С учетом этого становится очевидно, что хорошо, когда класс B может наследовать из двух базовых классов A1 и A2. Это называется множественным наследованием.

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

       class my_displayed_task: public displayed, public task        {           // текст пользователя        };        class my_task: public task {           // эта задача не изображается           // на экране, т. к. не содержит класс displayed           // текст пользователя        };        class my_displayed: public displayed        {           // а это не задача           // т. к. не содержит класс task           // текст пользователя        };

Если наследоваться может только один класс, то пользователю доступны только два из трех приведенных классов. В результате либо получается дублирование частей программы, либо теряется гибкость, а, как правило, происходит и то, и другое. Приведенный пример проходит в С++ безо всяких дополнительных расходов времени и памяти по сравнению с программами, в которых наследуется не более одного класса. Статический контроль типов от этого тоже не страдает.

Все неоднозначности выявляются на стадии трансляции:

         class task          {          public:            void trace ();            // ...          };          class displayed          {          public:            void trace ();            // ...          };          class my_displayed_task:public displayed, public task          {            // в этом классе trace () не определяется          };          void g ( my_displayed_task * p )          {            p -> trace ();  // ошибка: неоднозначность          }

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

      class my_displayed_task:public displayed, public task       {           // ...       public:           void trace ()           {             // текст пользователя             displayed::trace ();  // вызов trace () из displayed             task::trace ();       // вызов trace () из task           }           // ...      };

void g ( my_displayed_task * p )     {        p -> trace ();  // теперь нормально     }

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]