Виртуальное наследование, которое предложила Лаура, - это, конечно, решение проблемы. Но это не заканчивается наличием только одного InterfaceA. У него тоже есть «побочные эффекты», например. см. https://isocpp.org/wiki/faq/multiple-inheritance#mi-delegate-to-sister. Но если привыкнуть, это может пригодиться.
Если вы не хотите побочных эффектов, вы можете использовать шаблон:
struct InterfaceA
{
virtual void methodA() = 0;
};
template<class IA>
struct ClassA : public IA //IA is expected to extend InterfaceA
{
void methodA() { 5+1;}
};
struct InterfaceB : public InterfaceA
{
virtual void methodB() = 0;
};
struct ClassAB
: public ClassA<InterfaceB>
{
void methodB() {}
};
int main()
{
InterfaceB* test = new ClassAB();
test->methodA();
}
Итак, у нас ровно один родительский класс.
Но это выглядит уродливее, когда существует более одного «общего» класса (InterfaceA является «общим», потому что он находится над «страшным бриллиантом», см. Здесь https://isocpp.org/wiki/faq/multiple-inheritance от Лоры). Смотрите пример (что будет, если ClassA также реализует interfaceC):
struct InterfaceC
{
virtual void methodC() = 0;
};
struct InterfaceD : public InterfaceC
{
virtual void methodD() = 0;
};
template<class IA, class IC>
struct ClassA
: public IA //IA is expected to extend InterfaceA
, public IC //IC is expected to extend InterfaceC
{
void methodA() { 5+1;}
void methodC() { 1+2; }
};
struct InterfaceB : public InterfaceA
{
virtual void methodB() = 0;
};
struct ClassAB
: public ClassA<InterfaceB, InterfaceC> //we had to modify existing ClassAB!
{
void methodB() {}
};
struct ClassBD //new class, which needs ClassA to implement InterfaceD partially
: public ClassA<InterfaceB, InterfaceD>
{
void methodB() {}
void methodD() {}
};
Плохая вещь, что вам нужно было изменить существующий ClassAB. Но вы можете написать:
template<class IA, class IC = interfaceC>
struct ClassA
Тогда ClassAB остается без изменений:
struct ClassAB
: public ClassA<InterfaceB>
И у вас есть реализация по умолчанию для параметра шаблона IC.
Какой способ использовать - решать вам. Я предпочитаю шаблон, когда его легко понять. Довольно сложно привыкнуть, что B :: incrementAndPrint () и C :: incrementAndPrint () будут печатать разные значения (не ваш пример), смотрите это:
class A
{
public:
void incrementAndPrint() { cout<<"A have "<<n<<endl; ++n; }
A() : n(0) {}
private:
int n;
};
class B
: public virtual A
{};
class C
: public virtual A
{};
class D
: public B
: public C
{
public:
void printContents()
{
B::incrementAndPrint();
C::incrementAndPrint();
}
};
int main()
{
D d;
d.printContents();
}
И вывод:
A have 0
A have 1