18.5.3. Порядок вызова конструкторов и деструкторов
Виртуальные базовые классы всегда конструируются перед невиртуальными, вне зависимости от их расположения в иерархии наследования. Например, в приведенной иерархии у класса TeddyBear (плюшевый мишка) есть два виртуальных базовых: непосредственный – ToyAnimal (игрушечное животное) и экземпляр ZooAnimal, от которого унаследован класс Bear:
class Character { ... }; // персонаж
class BookCharacter : public Character { ... };
// литературный персонаж
class ToyAnimal { ... }; // игрушка
class TeddyBear : public BookCharacter,
public Bear, public virtual ToyAnimal
{ ... };
Эта иерархия изображена на рис. 18.5, где виртуальное наследование показано пунктирной стрелкой, а невиртуальное – сплошной.
Рис. 18.5. Иерархия виртуального наследования класса TeddyBear
Непосредственные базовые классы просматриваются в порядке их объявления при поиске среди них виртуальных. В нашем примере сначала анализируется поддерево наследования BookCharacter, затем Bear и наконец ToyAnimal. Каждое поддерево обходится в глубину, т.е. поиск начинается с корневого класса и продвигается вниз. Так, для поддерева BookCharacter сначала просматривается Character, а затем BookCharacter. Для поддерева Bear – ZooAnimal, а потом Bear.
При описанном алгоритме поиска порядок вызова конструкторов виртуальных базовых классов для TeddyBear таков: ZooAnimal, потом ToyAnimal.
После того как вызваны конструкторы виртуальных базовых классов , настает черед конструкторов невиртуальных, которые вызываются в порядке объявления: BookCharacter, затем Bear. Перед выполнением конструктора BookCharacter вызывается конструктор его базового класса Character.
Если имеется объявление:
TeddyBear Paddington;
то последовательность вызова конструкторов базовых классов будет такой:
ZooAnimal(); // виртуальный базовый класс Bear
ToyAnimal(); // непосредственный виртуальный базовый класс
Character(); // невиртуальный базовый класс BookCharacter
BookCharacter(); // непосредственный невиртуальный базовый класс
Bear(); // непосредственный невиртуальный базовый класс
TeddyBear(); // ближайший производный класс
причем за инициализацию ZooAnimal и ToyAnimal отвечает TeddyBear – ближайший производный класс объекта Paddington.
Порядок вызова копирующих конструкторов при почленной инициализации (и копирующих операторов присваивания при почленном присваивании) такой же. Гарантируется, что деструкторы вызываются в последовательности, обратной вызову конструкторов.
18.5.4. Видимость членов виртуального базового класса
Изменим наш класс Bear так, чтобы он имел собственную реализацию функции-члена onExhibit(), предоставляемой также ZooAnimal: bool Bear::onExhibit() { ... }
Теперь обращение к onExhibit() через объект Bear разрешается в пользу экземпляра, определенного в этом классе:
Bear winnie( "любитель меда" );
winnie.onExhibit(); // Bear::onExhibit()
Обращение же к onExhibit() через объект Raccoon разрешается в пользу функции-члена, унаследованной из ZooAnimal:
Raccoon meeko( "любитель всякой еды" );
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();