Интерфейсы и наследование С++

Я хочу иметь интерфейс IA и другой, который расширяет его IB.

A затем реализует IA, а B наследует A, а также реализует IB.

Однако, когда компиляция B получает ошибки, говорящие, что материал IA равен undefined, хотя A определил все: (

class IA
{
public:
    virtual ~IA(){}
    virtual void foo()=0;
};
class IB : public IA
{
public:
    virtual void bar()=0;
};


class A : public IA
{
public:
    A();
    void foo();
};
class B : public A, public IB
{
public:
    B();
    void bar();
};

ошибка C2259: 'B': не может создать экземпляр абстрактного класса
          за следующих членов:
          'void IA:: foo (void)': is abstract

Ответы

Ответ 1

Взгляните на часто задаваемые вопросы по С++ со следующего момента: https://isocpp.org/wiki/faq/multiple-inheritance#mi-diamond

В нем подробно описывается "страшный бриллиант" и виртуальное наследование.

Ответ 2

(Нет, A ничего не определяет. Вы объявили A() и void foo(), но вы не определили их.)

Реальная проблема - ваше множественное наследование.

       B
     /   \
   A      IB
  /         \
IA          IA

Как вы можете видеть, в вашем дереве наследования есть две "версии" IA, и только A реализует void IA::foo (хотя, как я уже отмечал выше, это даст вам ошибку компоновщика, как вы это делали 't определить реализацию).

void IB::IA::foo() остается нереализованным; таким образом, B сам "наследует абстрактность", и вы не можете его создать.

B также потребуется реализовать void foo(), или вы можете использовать виртуальное наследование, чтобы взломать его.

Ответ 3

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

В случае интерфейса/реализации параллельных иерархий каждое отношение наследования формы "любой класс → интерфейс" должно быть помечено как виртуальное: class A : public virtual IA; class B : public virtual IB, public A и class IB : public virtual IA.

Этот способ, вероятно (зависимый от реализации, но по крайней мере концептуально), дополнительный указатель хранится в каждом фактически производном классе, чтобы знать, где он должен найти свою виртуальную базу. Класс A, B и IB будет иметь указатель на IA, а класс B будет иметь указатель на IB. Каждая виртуальная база также будет иметь свою собственную таблицу vtable.

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

Затем вы можете продолжить параллелизм и добавить дополнительные классы C, IC и т.д., просто запомните:

Всякий раз, когда вы наследуете интерфейс, наследуйте фактически.

Кстати, классы COM имеют схожую конструкцию.

Ответ 4

У вас есть два копии foo(), один из которых унаследован от IB:: IA и один из A:: IA. Только один из них имеет не абстрактную версию в A.