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++中的接口继承的主要内容,如果未能解决你的问题,请参考以下文章