Класс не должен быть получен из более чем одного базового класса, который не является классом интерфейса
Класс не должен быть получен из нескольких базовых классов, которые не являются классами интерфейса.
Если класс наследует от нескольких неинтерфейсовых классов, то класс по существу имеет доступ к нескольким реализациям. Поддерживать код может быть сложно.
Когда класс наследует от нескольких неинтерфейсовых классов, существует вероятность того, что одна и та же функция представителя существует в этих базовых классах и должна быть переопределена в производном классе. Вероятность увеличивается, когда эти базовые классы сами наследуют от общего базового класса (алмазная структура).
Предположим, класс интерфейса Interface
имеет две конкретные реализации, Impl1
и Impl2
, и класс Final
происходит от обеих реализаций. Иерархия классов имеет эту алмазную структуру.
Могут возникнуть следующие проблемы:
Переопределения, необходимые в окончательном производном классе для значений:
Обе реализации Impl1
и Impl2
иметь копию всех методов класса Interface
. Чтобы определить, какая копия может быть вызвана через Final
объект, вы обычно создаете еще одно переопределение всех методов в Final
класс, где вы вызываете обе копии явным образом с помощью оператора разрешения возможностей ::
(или одну копию, если вы выберете). См. пример ниже.
Каждый раз, когда вы добавляете новую чистую виртуальную функцию к классу Interface
необходимо не только создать реализации в непосредственных производных классах, но и отслеживать всю иерархию классов и создавать переопределения этих реализаций в классе Final
.
Если исходный класс Interface
не является классом интерфейса, проблема еще острее. Если наследования не являются виртуальными, две копии методов Interface
неявно выполнены в Impl1
и Impl2
(алмазная проблема).
Конечный производный класс, ответственный за инициализацию всех классов в иерархии:
Чтобы избежать двойной инициализации в нескольких наследованиях, стандарт C++ требует, чтобы вы вызывали конструкторы всех предыдущих классов в самом производном классе.
В предыдущем примере Final
конструктор классов не только должен вызывать конструкторы Impl1
и Impl2
но и конструктор их родительского класса Interface
. Вы должны проследить за непосредственными родительскими элементами, чтобы определить, какие конструкторы вызывать в конечном производном классе.
Эти проблемы исчезают, если несколько наследований ограничены ситуациями, когда класс может происходить из нескольких классов, но только один из них может быть классом, не являющимся интерфейсом. Класс интерфейса является классом, который имеет только чистые виртуальные функции и представители данных, которые являются константами времени компиляции (static, contexpr
-с). Класс не имеет состояния, и его единственная цель должна быть реализована производными классами.
Множественное наследование было разработано для ситуаций, когда класс расширяет одну конкретную реализацию, но также реализует другие идеи, представленные классами интерфейса. Другие виды использования нескольких видов наследования могут привести к угрозе технического обслуживания.
Средство проверки помечает несколько наследований, где более одного базового класса является классом, не являющимся интерфейсом.
Класс интерфейса является таким, который имеет только чистые виртуальные функции и представители данных, которые являются константами времени компиляции (static, contexpr
-с). Любой конструктор или деструктор установлен в =default
или =delete
.
Если вы ожидаете нарушения правил, но не видите его, обратитесь к разделу «Стандартные нарушения кодирования не отображаются».
Группа: Производные классы |
Категория: Необходимый, Автоматизированный |