Класс Pegasus производится от двух базовых классов Horse и Bird, поэтому с исходным базовым классом Animal он связан двумя линиями наследования. Если для объекта класса Animal будет вызван метод GetAge(), то для преодоления неопределенности нужно точно указать, к какому базовому классу следует обращаться за этим методом, либо метод GetAge() следует заместить в классе Pegasus.
В нашем примере программы метод GetAge() замещается для класса Pegasus таким образом, что в нем явно указывается обращение к аналогичному методу конкретного базового класса.
Замещение функции с добавлением обращения к методу базового класса позволяет решить две проблемы. Во-первых, преодолевается неопределенность обращения к базовым классам; во-вторых, функцию можно заместить таким образом, что в производном классе при обращении к этой функции будут выполняться дополнительные операции, которых не было в базовом классе. Причем по желанию программиста эти дополнительные операции могут выполняться до вызова функции базового класса или после вызова с использованием значения, возвращенного функцией базового класса.
Конструктор класса Pegasus принимает пять параметров: цвет крылатого коня, его рост (в футах); логическую переменную, которая определяет, мигрирует сейчас это животное или мирно пасется на пастбище; число людей, верящих в существование Пегаса,
и возраст животного. В строке 87 конструктор инициализирует переменные, определенные в классе Horse (цвет, рост и возраст). В следующей строке инициализируется часть, относящаяся к классу Bird: цвет, миграции и возраст. Наконец, в строке 89 инициализируется переменная itsNumberBelievers, относящаяся непосредственно к классу Pegasus.
Вызов конструктора класса Horse в строке 87 выполняет операторы, записанные в строке 38. С помощью параметра age конструктор класса Horse инициализирует переменную itsAge, унаследованную классом Horse от класса Animal. Затем инициализируются две переменные-члена класса Horse — itsColor и itsHeight.
Вызов конструктора класса Bird в строке 88 выполняет операторы, записанные в строке 60. И в данном случае параметр age используется для инициализации переменной-члена, унаследованной классом Bird от класса Animal.
Обратите внимание, что значение параметра цвета объекта Pegasus используется для инициализации соответствующих переменных-членов обоих классов, Bird и Horse. Параметр age также инициализирует переменную itsAge обоих этих классов, унаследованную ими от базового класса Animal.
В листинге 13.5 решалась проблема неопределенности, а именно: от какого базового класса унаследована функция getAge() в объекте класса Pegasus. Но в действительности этот метод производится от одного общего базового класса Animal.
В C++ существует возможность указать, что мы имеем дело не с двумя одноименными классами, как показано в рис. 13.2, а с одним общим базовым классом (рис. 13.3).
Рис. 13.3. Виртуальное наследование
Для этого класс Animal нужно объявить как виртуальный базовый класс для двух производных классов, Horse и Bird. Класс Animal при этом не подвергается никаким изменениям. В классах Horse и Bird изменения состоят в том, что в их объявлении указывается виртуальность наследования от базового класса Animal. Класс Pegasus изменяется существенно.
Обычно конструктор класса инициализирует только собственные переменные и переменные-члены базового класса. Из этого правила делается исключение, если используется виртуальное наследование. Переменные основного базового класса инициализируются конструкторами не следующих производных от него классов, а тех, которые являются последними в иерархии классов. Поэтому класс Animal инициализируется не конструкторами классов Horse и Bird, а конструктором класса Pegasus. Конструкторы классов Horse и Bird также содержат команды инициализации базового класса Animal, но при создании объекта Pegasus эта инициализация перекрывается конструктором данного класса.
Листинг 13.6 представляет собой программный код из листинга 13.5, переписанный таким образом, чтобы можно было воспользоваться преимуществами виртуального наследования.
Листинг. 13.6. Пример использования виртуального наследования
1: // Листинг 13.6.
2: // Виртуальное наследование
3: #include
4:
5: typedef int HANDS;
6: enum COLOR { Red, Green, Blue, Yellow, White, Black, Brown } ;
7:
8: class Animal // общий базовый класс для двух производных классов horse и bird
Читать дальше