AUTOSAR C++14 Rule A10-1-1

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

Описание

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

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

Объяснение

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

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

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

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

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

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

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

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

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

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

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

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

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

Реализация Polyspace

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

Класс интерфейса является таким, который имеет только чистые виртуальные функции и представители данных, которые являются константами времени компиляции (static, contexpr-с). Любой конструктор или деструктор установлен в =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