exponenta event banner

Правило AUTOSAR C++ 14 A10-1-1

Класс не должен быть производным от более чем одного базового класса, который не является классом интерфейса

Описание

Определение правила

Класс не должен быть производным от более чем одного базового класса, который не является интерфейсным классом.

Объяснение

Если класс наследует от нескольких неинтерфейсных классов, класс по существу имеет доступ к нескольким реализациям. Ведение кода может быть затруднено.

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

Предположим, класс интерфейса Interface имеет две конкретные реализации, Impl1 и Impl2и класс Final происходит от обеих реализаций. Иерархия классов имеет эту структуру ромба.

Могут возникнуть следующие проблемы:

  • Переопределения, необходимые в конечном производном классе для значений:

    Обе реализации Impl1 и Impl2 иметь копию всех методов класса Interface. Чтобы определить, какая копия может быть вызвана через Final обычно создается еще одно переопределение всех методов в Final класс, в котором выполняется явный вызов обеих копий с помощью оператора разрешения области :: (или одну копию, если вы выберете). См. пример ниже.

    Каждый раз при добавлении в класс новой чистой виртуальной функции Interface, необходимо не только создавать реализации в непосредственных производных классах, но также отслеживать всю иерархию классов и создавать переопределения этих реализаций в классе Final.

    Если исходный класс Interface не является классом интерфейса, проблема стоит еще острее. Если наследства не являются виртуальными, две копии методов Interface неявно сделаны в Impl1 и Impl2 (алмазная проблема).

  • Окончательный производный класс, ответственный за инициализацию всех классов в иерархии:

    Чтобы избежать двойной инициализации при множественном наследовании, стандарт C++ требует вызова конструкторов всех предыдущих классов в самом производном классе.

    В предыдущем примере Final конструктор класса не только должен вызывать конструкторы Impl1 и Impl2 но также конструктор их родительского класса Interface. Для определения конструкторов, которые необходимо вызвать в конечном производном классе, необходимо выполнить трассировку за пределами непосредственных родителей.

Эти проблемы исчезают, если множественные наследования ограничиваются ситуациями, когда класс может быть производным от нескольких классов, но только один из них может быть классом без интерфейса. Класс интерфейса - это класс, который имеет только чистые виртуальные функции и элементы данных, которые являются константами времени компиляции (статические, contexpr-s). Класс не имеет состояния и его единственной целью является реализация производными классами.

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

Внедрение Polyspace

Средство проверки помечает несколько наследований, где несколько базовых классов не являются интерфейсными.

Класс интерфейса - это класс, который имеет только чистые виртуальные функции и элементы данных, которые являются константами времени компиляции (статические, contexpr-s). Любой конструктор или деструктор имеет значение =default или =delete.

Поиск неисправностей

Если вы ожидаете нарушения правила, но не видите его, обратитесь к разделу Нарушения стандартов кодирования не отображаются.

Примеры

развернуть все

class Interface {
    public:
    virtual void setVal()=0;
};

class Impl1: public Interface{
    int val1;
public:
    void setVal() {
        val1 = 0;
    }
};

class Impl2: public Interface{
    int val2;
public:
    void setVal() {
        val2 = 0;
    }
};

class Final: public Impl1, public Impl2 { //Noncompliant
public:
    void setVal() {
        Impl1::setVal();
        Impl2::setVal();
    } 
    
};

void main() {
    Final finalObj;
    finalObj.setVal();
}

В этом примере финал класса выводится из классов. Impl1 и Impl2. Оба класса Impl1 и Impl2 имеют элементы данных, которые не являются константами времени компиляции, и функции-члены, которые не являются чисто виртуальными функциями. Поэтому классы не являются интерфейсными классами. Наследование от двух классов, не являющихся интерфейсами, приводит к нарушению правил кодирования.

Проверить информацию

Группа: Производные классы
Категория: Обязательно, Автоматизировано
Представлен в R2020a