Понимание экспозиции Александреску о слабостях множественного наследования
ОБНОВЛЕНИЕ: я задал более узкий вопрос здесь.
На страницах 6-7 Modern C++ Design Андрей Александреску дает очень фундаментальное обсуждение сильных и слабых сторон двух C++
языковые возможности - множественное наследование и шаблоны - в отношении создания гибких конструкций. Он делает вывод:
Теперь сравните список недостатков множественного наследования со списком недостатков шаблонов. Интересно, что множественное наследование и шаблоны способствуют дополнительным компромиссам. Множественное наследование имеет скудную механику; шаблоны имеют богатую механику. Множественное наследование теряет информацию о типе, которая изобилует шаблонами. Специализация шаблонов не масштабируется, но множественное наследование довольно хорошо масштабируется. Вы можете предоставить только одно значение по умолчанию для функции-члена шаблона, но вы можете написать неограниченное количество базовых классов.
Я чувствую, что то, что говорит здесь Андрей, очень важно, но я не могу понять, что говорится, без каких-либо примеров, иллюстрирующих эти моменты. Этот вопрос просит предоставить простые примеры, иллюстрирующие эти моменты (пожалуйста, продолжайте читать).
Чтобы сделать вопрос более конкретным, я хотел бы попросить сосредоточиться на слабостях множественного наследования. Вот что Андрей должен сказать о них (текст в квадратных скобках - мой согласно моему пониманию контекста):
В такой обстановке [т.е. множественное наследование], [для создания гибкого
SmartPtr
,] пользователь будет создавать многопоточный класс интеллектуальных указателей с подсчетом ссылок, наследуя некоторыеBaseSmartPtr
класс и два класса:MultiThreaded
а такжеRefCounted
, Любой опытный дизайнер класса знает, что такой наивный дизайн не работает.Анализ причин, по которым множественное наследование не позволяет создавать гибкие конструкции, дает интересные идеи для достижения надежного решения. Проблемы с сборкой отдельных функций с использованием множественного наследования заключаются в следующем:
- Механика Не существует стандартного кода для сборки унаследованных компонентов контролируемым образом. Единственный инструмент, который объединяет BaseSmartPtr, MultiThreaded и RefCounting, - это языковой механизм, называемый множественным наследованием. Язык применяет простую суперпозицию при объединении базовых классов и устанавливает набор простых правил для доступа к их членам. Это недопустимо за исключением самых простых случаев. В большинстве случаев вам необходимо тщательно координировать работу унаследованных классов, чтобы получить желаемое поведение.
- Введите информацию. Базовые классы не имеют достаточно информации о типах для выполнения своих задач. Например, представьте, что вы пытаетесь реализовать глубокое копирование для своего класса интеллектуальных указателей, производного от базового класса DeepCopy. Но какой интерфейс у DeepCopy? Он должен создавать объекты типа, которого он не знает.
- Государственная манипуляция. Различные поведенческие аспекты, реализованные с помощью базовых классов, должны манипулировать одним и тем же состоянием. Это означает, что они должны использовать виртуальное наследование для наследования базового класса, который содержит состояние. Это усложняет проект и делает его более жестким, поскольку исходная посылка состояла в том, что пользовательские классы наследуют библиотечные классы, а не наоборот.
Я был бы очень признателен за простой пример для каждого из трех пунктов выше. В каждом примере будет показано как одно ограничение множественного наследования (например, плохая механика), так и то, как шаблоны не обладают этим ограничением (Андрей писал, что "множественное наследование и шаблоны способствуют дополнительному компромиссу").