C ++中多接口继承的歧义

时间:2012-04-27 15:03:15

标签: c++ inheritance interface multiple-inheritance virtual-inheritance

我制作了如下测试代码:

#include <iostream>
using namespace std;

#ifndef interface
#define interface struct
#endif

interface Base
{
    virtual void funcBase() = 0;
};

interface Derived1 : public Base
{
    virtual void funcDerived1() = 0;
};

interface Derived2 : public Base
{
    virtual void funcDerived2() = 0;
};

interface DDerived : public Derived1, public Derived2
{
    virtual void funcDDerived() = 0;
};

class Implementation : public DDerived
{
public:
    void funcBase() { cout << "base" << endl; }
    void funcDerived1() { cout << "derived1" << endl; }
    void funcDerived2() { cout << "derived2" << endl; }
    void funcDDerived() { cout << "dderived" << endl; }
};

int main()
{
    DDerived *pObject = new Implementation;
    pObject->funcBase();

    return 0;
}

我编写此代码的原因是测试是否可以在DDerived实例中调用函数funcBase()。当我尝试编译此代码时,我的C ++编译器(Visual Studio 2010)给了我一个编译错误消息。在我看来,这段代码没有问题,因为可以确定函数funcBase()将在接口DDerived的某个派生类中实现(因此覆盖),因为它是纯虚拟的。换句话说,类型Implementation *的任何指针变量都应该与派生Implentation并覆盖函数funcBase()的类的实例相关联。

我的问题是,为什么编译器会给我这样的错误信息?为什么C ++语法是这样定义的;即,将此案视为错误?如何让代码运行?我想允许接口的多重继承。当然,如果我使用“虚拟公共”或重新声明funcBase()中的函数Implementation,如

interface DDerived : public Derived1, public Derived2
{
    virtual void funcBase() = 0;
    virtual void funcDDerived() = 0;
};

然后一切都运行没有问题。

但是我不想这样做并寻找更方便的方法,因为虚拟继承可能会降低性能,如果类的继承关系非常复杂,重新声明是如此繁琐。除了使用虚拟继承之外,是否有任何方法可以在C ++中启用接口的多重继承?

4 个答案:

答案 0 :(得分:7)

正如您所定义的那样,您的对象结构如下所示:

enter image description here

这里重点是Implementation的每个实例都包含两个完全独立的Base实例。您提供了Base::funcBase的覆盖,但它不知道您是否试图覆盖funcBase通过Base继承的Derived1,或{ {1}}您通过Base继承。

是的,处理这个问题的干净方法是虚拟继承。这将改变你的结构,因此只有一个Base实例:

enter image description here

这几乎无疑是你真正想要的。是的,它在原始编译器和25 MHz 486等时代因性能问题而闻名。使用现代编译器和处理器,您不太可能遇到问题。

另一种可能性是某种基于模板的替代方案,但这往往遍及代码的其余部分 - 即,不是传递Derived2,而是编写一个模板,该模板将与提供的任何内容一起使用函数A,B和C,然后传递(相当于)Base *作为模板参数。

答案 1 :(得分:3)

C ++语言的设计方式是,在没有虚拟继承的第一种方法中,将有两个方法的父副本,它无法确定要调用哪一个。

虚拟继承是从多个基础继承相同函数的C ++解决方案,因此我建议使用该方法。

或者您考虑过不从多个基地继承相同的功能?您是否真的有一个派生类,您需要将其视为Derived1Derived2Base,具体取决于具体情况?

在这种情况下,详细阐述一个具体问题而不是一个人为的例子可能有助于提供更好的设计。

答案 2 :(得分:1)

DDerived *pObject = new Implementation;
pObject->funcBase();

这将为实现创建一个DDerived类型的指针。当你使用DDerived时,你真的只有一个指向接口的指针。

DDerived不了解funcBase的实现,因为在Derived1和Derived2中都定义了funcBase的模糊性。

这创造了一个继承钻石,这是真正导致问题的原因。

http://en.wikipedia.org/wiki/Diamond_problem

我还必须检查你在那里的“关键字”界面

这是一个特定于ms的扩展程序,已被visual studio识别

答案 3 :(得分:1)

我认为C ++ Standard 10.1.4 - 10.1.5可以帮助您理解代码中的问题。

class L { public: int next; /∗ ... ∗/ };
class A : public L { /∗...∗/ };
class B : public L { /∗...∗/ };
class C : public A, public B { void f(); /∗ ... ∗/ };
  

10.1.4 包含关键字virtual的基类说明符,   指定非虚基类。一个基类说明符   包含关键字virtual,指定虚拟基类。对于每一个   在类点阵中出现非虚拟基类   对于派生程度最高的类,最派生的对象(1.8)应包含   该类型的相应的不同基类子对象。对于每一个   指定虚拟的不同基类,最派生的对象   应包含该类型的单个基类子对象。 [示例:   对于类类型C的对象,每个不同的出现一个   C中的类格子中的(非虚拟)基类L对应   在C类对象中具有不同L子对象的一对一   鉴于上面定义的C类, C类的对象将有两个   L类次级对象如下所示。

     

10.1.5 在此类格子中,显式限定可用于指定   子对象是指。函数C :: f的主体可以参考   每个L子对象的下一个成员:void C :: f(){A :: next = B :: next; } //   合式。没有A ::或B ::限定符,C :: f的定义   由于含糊不清,上面会 形成错误

所以只需在调用pObject-&gt; funcBase()时添加限定符或以其他方式解决歧义。

pObject->Derived1::funcBase();

更新:阅读也非常有用,将是标准的 10.3虚拟功能

周末愉快:)