Missing virtual inheritance

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

Описание

Этот дефект возникает, когда:

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

    Для образца - класс 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"s копия m_b устанавливается равным 12. Однако Intermediate_left"s копия m_b все еще равен нулю. Поэтому, когда вы звоните d.get(), вы получаете нулевое значение.

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

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

  • Чтобы найти определение класса, на панели Source щелкните правой кнопкой мыши имя класса и выберите Go To Definition.

  • Чтобы перейти вверх по иерархии классов, сначала перейдите к определению промежуточного класса. В определении промежуточного класса щелкните правой кнопкой мыши имя базового класса и выберите Go To Definition.

Коррекция - сделайте оба наследования виртуальными

Одной из возможных коррекций является объявление обоих наследств от 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++
По умолчанию: Off
Синтаксис командной строки: MISSING_VIRTUAL_INHERITANCE
Влияние: Средний
Введенный в R2015b