system( "PAUSE" ) ; return 0 ;
}
В этом примере класс SleeperSofa наследует оба класса — Bed и Sofa. Это видно из их наличия в объявлении класса SleeperSofa, который наследует все члены от обоих базовых классов. Таким образом, допустимы оба вызова — как ss.sleep( ), так и ss.watchTV( ). Вы можете использовать SleeperSofa и как Bed, и как Sofa. Кроме того, класс SleeperSofa имеет собственные члены, например foldOut( ). В результате мы получим следующий вывод программы:
Смотрим телевизор
Раскладываем диван-кровать
Спим
Press any key to continue...
_________________
299 стр. Глава 26. Множественное наследование
►Устранение неоднозначностей множественного наследования...300
Будучи весьма мощной возможностью языка, множественное наследование может стать в то же время и источником проблем. Одну из них можно увидеть уже в предыдущем примере. Обратите внимание, что оба класса — Bed и Sofa — содержат член weight ( вес ). Это логично, потому что они оба имеют некоторый вполне измеримый вес. Вопрос: какой именно член weight наследует класс SleeperSofa?
Ответ прост: оба. Класс SleeperSofa наследует отдельный член Bed::weight и отдельный член Sofa::weight. Поскольку они оба имеют одно и то же имя, обращения к weight теперь являются двузначными, если только не указывать явно, к какому именно weight мы намерены обратиться. Это демонстрирует следующий фрагмент кода:
#include
void fn( )
{
SleeperSofa ss ;
cout << "Beс = "
<< ss.weight /* неправильно — какой именно вес? */
<< "\n" ;
}
Теперь в программе нужно явно указывать, какая именно переменная weight нужна, используя для этого имя базового класса. Приведённый ниже пример вполне корректен.
#include
void fn( )
{
SleeperSofa ss ;
cout << "Вес дивана = "
<< ss.Sofa::weight /*укажем, какой именно вес */
<< "\n" ;
}
Хотя такое решение и устраняет ошибку, указание имени базового класса во внешнем приложении нежелательно: ведь при этом информация о внутреннем устройстве класса должна присутствовать за его пределами. В нашем примере функция fn( ) должна располагать сведениями о том, что класс SleeperSofa наследуется от класса Sofa. Такие конфликты имён невозможны при одиночном наследовании, но служат постоянным источником неприятностей при наследовании множественном.
_________________
300 стр. Часть 5. Полезные особенности
►Виртуальное наследование...301
В случае класса SleeperSofa конфликт имён weight является, по сути, небольшим недоразумением. Ведь на самом деле диван-кровать не имеет отдельного веса как кровать, и отдельного веса как диван. Конфликт возник потому, что такая иерархия классов не вполне адекватно описывает реальный мир. Дело в том, что разложение на классы оказалось неполным.
Если немного подумать над этой проблемой, становится ясно, что и кровать и диван являются частными случаями некоторой более фундаментальной концепции мебели ( думаю, можно было предложить нечто ещё более фундаментальное, но для нас достаточно ограничиться мебелью ). Вес является свойством любой мебели, что показано на рис. 26.2.
Рис. 26.2. Выделение общих свойств кровати и дивана
«Если отделить класс Furniture ( мебель ), конфликт имён будет устранен. Итак, с чувством глубокого удовлетворения и облегчения, в предвкушении успеха реализуем новую иерархию классов в программе MultipleInheritanceFactoring, которую вы можете найти на прилагаемом компакт-диске:»
[Диск]
//
/* MultipleInheritanceFactoring — класс, являющийся */
/* наследником нескольких */
/* базовых классов */
//
#include
#include
#include
using namespace std ;
/* Furniture — фундаментальная концепция, обладающая весом */
class Furniture
{
_________________
301 стр. Глава 26. Множественное наследование
public :
Furniture( int w ) : weight( w ) { }
int weight ;
} ;