Урок №161. Множественное наследование

   | 

   | 

 Обновлено 14 Сен 2018  | 

 327

До сих пор мы рассматривали только одиночные наследования, когда дочерний класс имеет только одного родителя. Однако C++ предоставляет возможность выполнения множественного наследования. Множественное наследование позволяет одному дочернему классу иметь несколько родителей.

Предположим, мы хотим написать программу для отслеживания работы учителей. Учитель — это Human. Тем не менее, он также является Сотрудником (Employee).

Множественное наследование может быть использовано для создания класса Teacher, который будет наследовать свойства как Human, так и Employee. Для использования множественного наследования нужно просто указать через запятую тип наследования и второй родительский класс:

Здесь мы используем наследование типа public.

Проблемы с множественным наследованием

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

Во-первых, может возникнуть неоднозначность, когда несколько родительских классов имеют метод с одним и тем же именем. Например:

При компиляции c54G.getID() компилятор смотрит, есть ли у WirelessAdapter метод getID(). Нету. Тогда компилятор двигается по цепочке наследования вверх и смотрит есть ли этот метод в каком-либо из родительских классов. И здесь возникает проблема – getID() есть как у USBDevice, так и у NetworkDevice. Следовательно, вызов этого метода приведет к неоднозначности и мы получим ошибку, так как компилятор не будет знать какую версию getID() вызывать.

Тем не менее, есть способ обойти эту проблему. Мы можем явно указать, какую версию getID() следует вызывать:

Хотя это решение довольно простое, но всё может стать намного сложнее, если наш класс будет иметь от 4 родительских классов, которые в свою очередь будут иметь свои родительские классы. Возможность возникновения конфликтов имён увеличивается экспоненциально с каждым добавленным родительским классом и в каждом из таких случаев нужно будет явно указывать версии методов, которые следует вызывать, дабы избежать возможности возникновения конфликтов имён.

Во-вторых, более серьезной проблемой является «алмаз смерти» (или еще «алмаз обреченности»). Это ситуация, когда один класс имеет 2 родительских класса, каждый из которых в свою очередь наследует свойства одного и того же родительского класса. Иллюстративно мы получаем форму алмаза.



Например, рассмотрим следующие классы:

Сканеры и принтеры — это устройства, которые получают питание от розетки, поэтому они наследуют свойства PoweredDevice. Однако копировальный аппарат включает в себя функции как сканеров, так и принтеров.

 

В этом контексте возникает много проблем, включая неоднозначность при вызове методов и копирование данных PoweredDevice в класс Copier дважды. Хотя большинство из этих проблем можно решить с помощью явного указания, поддержка и обслуживание такого кода может привести к непредсказуемым временным затратам. Мы поговорим детальнее о способах решения проблемы «алмаза смерти» в одном из следующих уроков.

Так стоит ли использовать множественное наследование?

Большинство задач, решаемых с помощью множественного наследования, могут быть решены и с использованием одиночного наследования. Многие объектно-ориентированные языки программирования (например, Smalltalk, PHP) даже не поддерживают множественное наследование. Многие, относительно современные языки, такие как Java и C#, ограничивают классы одиночным наследованием обычных классов, но допускают множественное наследование интерфейсных классов (об этом поговорим позже). Суть идеи, запрещающей множественное наследование в этих языках, заключается в том, что это лишняя сложность, которая вызывает проблем больше, чем решает.

Многие опытные программисты считают, что множественное наследование в C++ следует избегать любой ценой из-за потенциальных проблем, которые могут возникнуть. Однако остается вероятность, когда множественное наследование будет лучшим решением, нежели придумывание двухуровневых костылей.

Стоит заметить, что вы сами уже использовали классы, написанные с использованием множественного наследования, даже не подозревая об этом: такие объекты как std::cin и std::cout библиотеки iostream реализованы с использованием множественного наследования!

Правило: Используйте множественное наследование только в крайних случаях, когда задачу нельзя решить одиночным наследованием, либо другим альтернативным способом (без изобретения велосипедов).

Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (6 оценок, среднее: 4,33 из 5)
Загрузка...
Подписаться на обновления:

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

ПОДПИСЫВАЙТЕСЬ

НА КАНАЛ RAVESLI В TELEGRAM

@ravesli

ПОДПИСАТЬСЯ БЕСПЛАТНО