C++ 中的接口继承

问题描述 投票:0回答:4

我有以下班级结构:

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

现在以下代码无法编译:

int main()
{
    InterfaceB* test = new ClassAB();
    test->methodA();
}

编译器说方法

methodA()
是虚拟的并且没有实现。我认为它是在
ClassA
中实现的(它实现了
InterfaceA
)。 有谁知道我的错在哪里吗?

c++ inheritance interface
4个回答
23
投票

那是因为你有两份

InterfaceA
。请参阅此以获得更详细的解释:https://isocpp.org/wiki/faq/multiple-inheritance(您的情况类似于“可怕的钻石”)。

从InterfaceA继承ClassA时需要添加关键字

virtual
。从InterfaceA继承InterfaceB时还需要添加
virtual


10
投票

Laura 建议的虚拟继承当然是问题的解决方案。但最终并不会只有一个 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 -继承,由劳拉发布)。参见示例(如果 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::printAndIncrement() 和 C::printAndIncrement() 将打印不同的值(不是您的示例),请参阅:

class A
{
public:
  void printAndIncrement() { 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::printAndIncrement();
    C::printAndIncrement();
  }
};

int main()
{
  D d;
  d.printContents();
}

输出:

A have 0
A have 1

5
投票

之所以存在这个问题,是因为C++并没有真正的接口,只有具有多重继承的纯虚类。编译器不知道在哪里找到

methodA()
的实现,因为它是由
ClassAB
的不同基类实现的。您可以通过在
methodA()
中实现
ClassAB()
来调用基本实现来解决此问题:

class ClassAB : public ClassA, public InterfaceB
{ 
    void methodA()
    {
        ClassA::methodA();
    }

    void methodB(); 
}

2
投票

这里有一颗可怕的钻石。 InterfaceB 和 ClassA 必须从 InterfaceA 继承 否则,ClassAB 有两个 MethodA 副本,其中之一仍然是纯虚拟的。您不应该能够实例化此类。即使你是 - 编译器也无法决定调用哪个 MethodA。

© www.soinside.com 2019 - 2024. All rights reserved.