Interface Inheritance in C++
Solution 1
That is because you have two copies of InterfaceA
. See this for a bigger explanation: https://isocpp.org/wiki/faq/multiple-inheritance (your situation is similar to 'the dreaded diamond').
You need to add the keyword virtual
when you inherit ClassA from InterfaceA. You also need to add virtual
when you inherit InterfaceB from InterfaceA.
Solution 2
Virtual inheritance, which Laura suggested, is, of course, the solution of the problem. But it doesn't end up in having only one InterfaceA. It has "side-effects" too, for ex. see https://isocpp.org/wiki/faq/multiple-inheritance#mi-delegate-to-sister. But if get used to it, it may come in handy.
If you don't want side effects, you may use template:
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();
}
So, we are having exactly one parent class.
But it looks more ugly when there is more than one "shared" class (InterfaceA is "shared", because it is on top of "dreaded diamond", see here https://isocpp.org/wiki/faq/multiple-inheritance as posted by Laura). See example (what will be, if ClassA implements interfaceC too):
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() {}
};
The bad thing, that you needed to modify existing ClassAB. But you can write:
template<class IA, class IC = interfaceC>
struct ClassA
Then ClassAB stays unchanged:
struct ClassAB
: public ClassA<InterfaceB>
And you have default implementation for template parameter IC.
Which way to use is for you to decide. I prefer template, when it is simple to understand. It is quite difficult to get into habit, that B::incrementAndPrint() and C::incrementAndPrint() will print different values (not your example), see this:
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();
}
And the output:
A have 0
A have 1
Solution 3
This problem exists because C++ doesn't really have interfaces, only pure virtual classes with multiple inheritance. The compiler doesn't know where to find the implementation of methodA()
because it is implemented by a different base class of ClassAB
. You can get around this by implementing methodA()
in ClassAB()
to call the base implementation:
class ClassAB : public ClassA, public InterfaceB
{
void methodA()
{
ClassA::methodA();
}
void methodB();
}
Solution 4
You have a dreaded diamond here. InterfaceB and ClassA must virtually inherit from InterfaceA Otherwise you ClassAB has two copies of MethodA one of which is still pure virtual. You should not be able to instantiate this class. And even if you were - compiler would not be able to decide which MethodA to call.
MOnsDaR
Professional Software-Developer since 2006 - C++ (Professional) - C# (Basics) - Python (Basics) - Javascript (Basics) - jQuery and jQuery Mobile (Basics)
Updated on July 09, 2022Comments
-
MOnsDaR almost 2 years
I have the following class structure:
class InterfaceA { virtual void methodA =0; } class ClassA : public InterfaceA { void methodA(); } class InterfaceB : public InterfaceA { virtual void methodB =0; } class ClassAB : public ClassA, public InterfaceB { void methodB(); }
Now the following code is not compilable:
int main() { InterfaceB* test = new ClassAB(); test->methodA(); }
The compiler says that the method
methodA()
is virtual and not implemented. I thought that it is implemented inClassA
(which implements theInterfaceA
). Does anyone know where my fault is? -
palantus over 14 yearsIf C++'s rules are similar to Java's, the compiler still wouldn't know about it because the pointer was declared as
InterfaceB
, which has nomethodA
. -
jmucchiello over 14 yearsmmyers: That's wrong for both C++ and Java. InterfaceB inherits from InterfaceA where methodA is defined.
-
stonemetal over 14 yearsInterfaceB inherits from interfaceA so it should have methodA
-
Daniel Rodriguez over 14 yearsThanks for the clarification laura.
-
MOnsDaR over 14 yearsThe above solution was my first approach to solve the issue. It worked, but it was very bad to handle. (If the Interface A changes, I had to do the changes in every inherited class too)
-
johnbakers almost 11 yearsI thought that once a function is declared
virtual
is is always virtual throughout the class hierarchy whether derived classes actually usevirtual
or not when defining it