C++中的接口继承

Posted

技术标签:

【中文标题】C++中的接口继承【英文标题】:Interface Inheritance in C++ 【发布时间】:2010-12-15 23:01:40 【问题描述】:

我有以下类结构:

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)。 有谁知道我的错在哪里?

【问题讨论】:

【参考方案1】:

那是因为您有两份InterfaceA。请参阅此以获得更大的解释:https://isocpp.org/wiki/faq/multiple-inheritance(您的情况类似于“可怕的钻石”)。

从InterfaceA继承ClassA时需要添加关键字virtual。从 InterfaceA 继承 InterfaceB 时,还需要添加virtual

【讨论】:

感谢劳拉的澄清。 我认为一旦一个函数被声明virtual在整个类层次结构中总是虚拟的,无论派生类在定义它时是否实际使用virtual【参考方案2】:

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-inheritance 由 Laura 发布)。查看示例(如果 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

【讨论】:

【参考方案3】:

存在这个问题是因为 C++ 没有真正的接口,只有具有多重继承的纯虚拟类。编译器不知道在哪里可以找到methodA() 的实现,因为它是由ClassAB 的不同基类实现的。您可以通过在 ClassAB() 中实现 methodA() 来调用基本实现来解决此问题:

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

    void methodB(); 

【讨论】:

如果 C++ 的规则与 Java 的规则相似,编译器仍然不会知道它,因为指针被声明为 InterfaceB,它没有 methodA mmyers:这对 C++ 和 Java 来说都是错误的。 InterfaceB 继承自 InterfaceA,其中定义了 methodA。 InterfaceB 继承自 interfaceA 所以它应该有 methodA 上述解决方案是我解决问题的第一种方法。它有效,但处理起来非常糟糕。 (如果接口 A 发生变化,我也必须在每个继承的类中进行更改)【参考方案4】:

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

【讨论】:

以上是关于C++中的接口继承的主要内容,如果未能解决你的问题,请参考以下文章

C++ 中的类接口继承

C++ 中的流利接口和继承

C++ 继承、接口

C++ 中的继承默认访问修饰符

C++中的多重继承

我可以直接继承实现以满足c ++中的接口吗