Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология Образование Политология Производство Психология Стандартизация Технологии |
Видимость членов виртуального базового класса
Изменим наш класс Bear так, чтобы он имел собственную реализацию функции-члена onExhibit(), предоставляемой также ZooAnimal: bool Bear:: onExhibit() {... } Теперь обращение к onExhibit() через объект Bear разрешается в пользу экземпляра, определенного в этом классе:
winnie.onExhibit(); // Bear:: onExhibit() Обращение же к onExhibit() через объект Raccoon разрешается в пользу функции-члена, унаследованной из ZooAnimal:
meeko.onExhibit(); // ZooAnimal:: onExhibit() Производный класс Panda наследует члены своих базовых классов. Их можно отнести к одной из трех категорий: · члены виртуального базового класса ZooAnimal, такие, как name() и family(), не замещенные ни в Bear, ни в Raccoon; · член onExhibit() виртуального базового класса ZooAnimal, наследуемый при обращении через Raccoon и замещенный в классе Bear; · специализированные в классах Bear и Raccoon экземпляры функции print() из ZooAnimal. Можно ли, не опасаясь неоднозначности, напрямую обращаться к унаследованным членам из области видимости класса Panda? В случае невиртуального наследования – нет: все неквалифицированные ссылки на имя неоднозначны. Что касается виртуального наследования, то прямое обращение допустимо к любым членам из первой и второй категорий. Например, дан объект класса Panda: Panda spot( " Spottie" ); Тогда инструкция spot.name(); вызывает разделяемую функцию-член name() виртуального базового ZooAnimal, а инструкция spot.onExhibit(); вызывает функцию-член onExhibit() производного класса Bear. Когда два или более экземпляров члена наследуются разными путями (это относится не только к функциям-членам, но и к данным-членам, а также к вложенным типам) и все они представляют один и тот же член виртуального базового класса, неоднозначности не возникает, поскольку существует единственный разделяемый экземпляр (первая категория). Если один экземпляр представляет член виртуального базового, а другой – член унаследованного от него класса, то неоднозначности также не возникает: специализированному экземпляру из производного класса отдается предпочтение по сравнению с разделяемым экземпляром из виртуального базового (вторая категория). Но если оба экземпляра представляют члены производных классов, то прямое обращение неоднозначно. Лучше всего разрешить эту ситуацию, предоставив замещающий экземпляр в производном классе (третья категория). Например, при невиртуальном наследовании неквалифицированное обращение к onExhibit() через объект Panda неоднозначно:
yolo.onExhibit(); В данном случае все унаследованные экземпляры имеют равные приоритеты при разрешении имени, поэтому неквалифицированное обращение приводит к ошибке компиляции из-за неоднозначности (см. раздел 18.4.1). При виртуальном наследовании члену, унаследованному из виртуального базового класса, приписывается меньший приоритет, чем члену с тем же именем, замещенному в производном. Так, унаследованному от Bear экземпляру onExhibit() отдается предпочтение перед экземпляром из ZooAnimal, унаследованному через Raccoon:
yolo.onExhibit(); Если два или более классов на одном и том же уровне наследования замещают некоторый член виртуального базового, то в производном они будут иметь одинаковый вес. Например, если в Raccoon также определен член onExhibit(), то при обращении к нему из Panda придется квалифицировать имя с помощью оператора разрешения области видимости:
} Упражнение 18.13 Дана иерархия классов:
class Final: public MI, public Class {... }; (a)В каком порядке вызываются конструкторы и деструкторы при определении объекта Final? (b)Сколько подобъектов класса Base содержит объект Final? А сколько подобъектов Class? (c)Какие из следующих присваиваний вызывают ошибку компиляции?
(ii) pc = new Final; (iv) pd2 = pmi; Упражнение 18.14 Дана иерархия классов:
class VMI: public Derived1, public Derived2 {}; К каким из унаследованных членов можно обращаться из класса VMI, не квалифицируя имя? А какие требуют квалификации? Упражнение 18.15 Дан класс Base с тремя конструкторами:
}; Определите соответствующие конструкторы для каждого из следующих классов:
(c) class Final: public VMI {... }; Пример множественного виртуального наследования A Мы продемонстрируем определение и использование множественного виртуального наследования, реализовав иерархию шаблонов классов Array (см. раздел 2.4) на основе шаблона Array (см. главу 16), модифицированного так, чтобы он стал конкретным базовым классом. Перед тем как приступать к реализации, поговорим о взаимосвязях между шаблонами классов и наследованием. Конкретизированный экземпляр такого шаблона может выступать в роли явного базового класса: class IntStack: private Array< int> {}; Разрешается также произвести его от не шаблонного базового класса:
class Derived: public Base {}; Шаблон может выступать одновременно в роли базового и производного классов:
class Array_RC: public virtual Array< Type> {}; В первом примере конкретизированный типом int шаблон Array служит закрытым базовым классом для не шаблонного IntStack. Во втором примере не шаблонный Base служит базовым для любого класса, конкретизированного из шаблона Derived. В третьем примере любой конкретизированный из шаблона Array_RC класс является производным от класса, конкретизированного из шаблона Array. Так, инструкция Array_RC< int> ia; конкретизирует экземпляры шаблонов Array и Array_RC. Кроме того, сам параметр-шаблон может служить базовым классом [MURRAY93]:
class Persistent: public Type {... }; в данном примере определяется производный устойчивый (persistent) подтип для любого конкретизированного типа. Как отмечает Мюррей (Murray), на Type налагается неявное ограничение: он должен быть типом класса. Например, инструкция Persistent< int > pi; // ошибка приводит к ошибке компиляции, поскольку встроенный тип не может быть объектом наследования. Шаблон, выступающий в роли базового класса, должен квалифицироваться полным списком параметров. Если имеется определение: template < class T> class Base {}; то необходимо писать:
class Derived: public Base< Type> {}; Такая запись неправильна:
class Derived: public Base {}; В следующем разделе шаблон Array, определенный в главе 16, выступает в роли виртуального базового класса для подтипа Array, контролирующего выход за границы массива; для отсортированного подтипа Array; для подтипа Array, который обладает обоими указанными свойствами. Однако первоначальное определение шаблона класса Array для наследования не подходит: · все его члены и вспомогательные функции объявлены закрытыми, а не защищенными; · ни одна из зависящих от типа функций-членов, скажем оператор взятия индекса, не объявлена виртуальной. Означает ли это, что наша первоначальная реализация была неправильной? Нет. Она была верной на том уровне понимания, которым мы тогда обладали. При реализации шаблона класса Array мы еще не осознали необходимость специализированных подтипов. Теперь, однако, определение шаблона придется изменить так (реализации функций-членов при этом останутся теми же):
#endif Одна из проблем, связанных с таким переходом к полиморфизму, заключается в том, что реализация оператора взятия индекса перестала быть встроенной и сводится теперь к значительно более дорогому вызову виртуальной функции. Так, в следующей функции, на какой бы тип она ни ссылалась, было бы достаточно встроенного чтения элемента:
} Для повышения производительности мы включили встроенную функцию-член at(), обеспечивающую прямое чтение элемента. |
Последнее изменение этой страницы: 2019-04-09; Просмотров: 270; Нарушение авторского права страницы