Interface Inheritance in C++

23,999

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.

Share:
23,999
MOnsDaR
Author by

MOnsDaR

Professional Software-Developer since 2006 - C++ (Professional) - C# (Basics) - Python (Basics) - Javascript (Basics) - jQuery and jQuery Mobile (Basics)

Updated on July 09, 2022

Comments

  • MOnsDaR
    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 in ClassA (which implements the InterfaceA). Does anyone know where my fault is?

  • palantus
    palantus over 14 years
    If 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 no methodA.
  • jmucchiello
    jmucchiello over 14 years
    mmyers: That's wrong for both C++ and Java. InterfaceB inherits from InterfaceA where methodA is defined.
  • stonemetal
    stonemetal over 14 years
    InterfaceB inherits from interfaceA so it should have methodA
  • Daniel Rodriguez
    Daniel Rodriguez over 14 years
    Thanks for the clarification laura.
  • MOnsDaR
    MOnsDaR over 14 years
    The 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
    johnbakers almost 11 years
    I thought that once a function is declared virtual is is always virtual throughout the class hierarchy whether derived classes actually use virtual or not when defining it