exponenta event banner

Отсутствует виртуальное наследование

Базовый класс наследуется виртуально и невиртуально в одной иерархии

Описание

Этот дефект возникает в следующих случаях:

  • Класс является производным от нескольких базовых классов, и некоторые из этих базовых классов сами являются производными от общего базового класса.

    Например, класс Final является производным от двух классов, Intermediate_left и Intermediate_right. Оба Intermediate_left и Intermediate_right являются производными от общего класса, Base.

  • По крайней мере одно из наследований от общего базового класса virtual и по крайней мере один не является virtual.

    Например, наследование Intermediate_right от Base является virtual. Наследование Intermediate_left от Base не является virtual.

Риск

При появлении этого дефекта в конечном производном объекте класса появляется несколько копий элементов данных базового класса. Для получения доступа к правильной копии элемента данных базового класса необходимо соответствующим образом определить имя элемента и метода в окончательном производном классе. Разработка подвержена ошибкам.

Например, при возникновении дефекта в объекте класса появляются две копии элементов данных базового класса. Final. Если имена методов в классе не определены соответствующим образом Final, можно назначить значение Base элемент данных, но не извлекает то же самое значение.

  • Значение присваивается с помощью Base способ, доступ к которому осуществляется через Intermediate_left. Поэтому значение присваивается одной копии Base член.

  • Вы извлекаете значение с помощью Base способ, доступ к которому осуществляется через Intermediate_right. Таким образом, вы извлекаете другую копию Base член.

Зафиксировать

Объявить все промежуточные наследования как virtual когда класс является производным от нескольких базовых классов, которые сами являются производными от общего базового класса.

Если вы действительно хотите несколько копий Base элементы данных, представленные в промежуточных производных классах, используют агрегирование вместо наследования. Например, объявить два объекта класса Intermediate_left и Intermediate_right в Final класс.

Примеры

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

#include <stdio.h>
class Base {
public:
    explicit Base(int i): m_b(i) {};
    virtual ~Base() {};
    virtual int get() const {
        return m_b;
    }
    virtual void set(int b) {
        m_b = b;
    }
private:
    int m_b;
};

class Intermediate_left: virtual public Base {
public:
    Intermediate_left():Base(0), m_d1(0) {};
private:
    int m_d1;
};

class Intermediate_right: public Base {
public:
    Intermediate_right():Base(0), m_d2(0) {};
private:
    int m_d2;
};

class Final: public Intermediate_left, Intermediate_right {
public:
    Final(): Base(0), Intermediate_left(), Intermediate_right() {};
    int get() const {
        return Intermediate_left::get();
    }
    void set(int b) {
        Intermediate_right::set(b);
    }
    int get2() const {
        return Intermediate_right::get();
    }
};

int main(int argc, char* argv[]) {
    Final d;
    int val = 12;
    d.set(val);
    int res = d.get();
    printf("d.get=%d\n",res);             // Result: d.get=0
    printf("d.get2=%d\n",d.get2());       // Result: d.get2=12
    return res;
}

В этом примере: Final является производным от обоих Intermediate_left и Intermediate_right. Intermediate_left является производным от Base в не-virtual манера и Intermediate_right является производным от Base в virtual манера. Поэтому две копии базового класса и элемента данных m_b присутствуют в конечном производном классе,

Оба производных класса Intermediate_left и Intermediate_right не переопределять Base методы классов get и set. Однако Final переопределяет оба метода. В переопределенном get метод, он вызывает Base::get через Intermediate_left. В переопределенном set метод, он вызывает Base::set через Intermediate_right.

После заявления d.set(val), Intermediate_right«» копия m_b имеет значение 12. Однако Intermediate_left«» копия m_b по-прежнему равен нулю. Поэтому при звонке d.get(), получается нулевое значение.

Использование printf операторы, можно видеть, что извлекается значение, отличное от заданного значения.

Дефект появляется в окончательном определении производного класса и в названии класса, производного фактически от общего базового класса. Ниже приведены некоторые советы по навигации в исходном коде:

  • Чтобы найти определение класса, на панели «Источник» щелкните правой кнопкой мыши имя класса и выберите «Перейти к определению».

  • Для перемещения вверх по иерархии классов сначала перейдите к определению промежуточного класса. В определении промежуточного класса щелкните правой кнопкой мыши имя базового класса и выберите «Перейти к определению».

Исправление - сделайте оба наследства виртуальными

Одним из возможных исправлений является объявление обоих наследств от Base как virtual.

Несмотря на переопределенное get и set методы в Final все еще звоните Base::get и Base::set через различные классы, только одна копия m_b существует в Final.

#include <stdio.h>
class Base {
public:
    explicit Base(int i): m_b(i) {};
    virtual ~Base() {};
    virtual int get() const {
        return m_b;
    }
    virtual void set(int b) {
        m_b = b;
    }
private:
    int m_b;
};

class Intermediate_left: virtual public Base {
public:
    Intermediate_left():Base(0), m_d1(0) {};
private:
    int m_d1;
};

class Intermediate_right: virtual public Base {
public:
    Intermediate_right():Base(0), m_d2(0) {};
private:
    int m_d2;
};

class Final: public Intermediate_left, Intermediate_right {
public:
    Final(): Base(0), Intermediate_left(), Intermediate_right() {};
    int get() const {
        return Intermediate_left::get();
    }
    void set(int b) {
        Intermediate_right::set(b);
    }
    int get2() const {
        return Intermediate_right::get();
    }
};

int main(int argc, char* argv[]) {
    Final d;
    int val = 12;
    d.set(val);
    int res = d.get();
    printf("d.get=%d\n",res);             // Result: d.get=12
    printf("d.get2=%d\n",d.get2());       // Result: d.get2=12
    return res;
}

Информация о результатах

Группа: Объектно-ориентированная
Язык: C++
По умолчанию: Откл.
Синтаксис командной строки: MISSING_VIRTUAL_INHERITANCE
Воздействие: среднее
Представлен в R2015b