Interface Inheritance in C++

岁酱吖の 提交于 2019-12-09 05:09:05

问题


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?


回答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.




回答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



回答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(); 
}



回答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.



来源:https://stackoverflow.com/questions/1723469/interface-inheritance-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!