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 в non-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 операторы, вы видите, что получаете значение, которое отличается от значения, которое вы устанавливаете.

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

  • Чтобы найти определение класса, на панели 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