具有两个接口的多重继承,一个派生自第一个接口,一个派生 class 实现第一个接口

Multiple inheritance with two interfaces, one which derives from first, and a derived class implementing the first interface

抱歉描述不当。这是问题所在:

class PureVirtualBase {
public:
    virtual ~PureVirtualBase() {}
    virtual int IntFn() = 0;
};

class PureVirtualDerivedBase : public PureVirtualBase {
public:
    virtual ~PureVirtualDerivedBase() {}
    virtual int OtherIntFn() = 0;
};

class Foo : public PureVirtualBase {
public:
    virtual int IntFn() { return intVal; }
protected:
    int intVal;
};

class Bar : public Foo, public PureVirtualDerivedBase {
public
    virtual int OtherIntFn() { return 123; }
};

由于“'Bar”而失败:由于以下成员无法实例化抽象 class:'int PureVirtualBase::IntFn(void) 是抽象的

我不太确定如何更正这个问题。我原以为 Bar 很好,因为它继承了 Foo 的 IntFn 实现(并根据 PureVirtualDerivedBase 的要求添加了 OtherIntFn)。我试过使继承虚拟化(public virtual Foo,public virtual PureVirtualBase),但这没有用。

有什么想法吗?提前致谢。

这是 the diamond problem 的细微变化。基本上在 c++ 中,每个继承路径都是单独遵循的,这意味着 class Bar 有一个 Foo 的副本和一个 PureVirtualDerivedBase 的副本。这解释了您面临的问题。

但是,我看不出从 PureVirtualDerivedBase 明确导出 Bar 的意义,因为 Foo 已经有了它。

我对这里的 class 结构设计的主要担忧是,在您提供的示例中,PureVirtualDerivedBasePureVirtualBase 继承似乎是多余的。通过在另一个抽象 class 中派生一个抽象 class 可以使您的继承树混乱,在最坏的情况下,您最终可能不得不为您在中声明的抽象方法创建重复的实现基地 class.

在这种情况下,您必须在 Bar 中重新实现 IntFn(),因为继承 PureVirtualDerivedBase 明确要求您实现与其关联的所有虚拟方法,无论它们是否已经已在 Foo.

实施

我的建议是从 PureVirtualDerivedBase 中删除 PureVirtualBase 继承,只继承 class 中实际实现抽象方法的抽象 class。为清楚起见,在可以避免的情况下避免使接口依赖于其他接口也是一种很好的做法。当您希望为派生的 class.

实现不同的功能时,请尝试使接口尽可能独立并从多个接口继承

错误在于如何应用虚拟继承。

具体来说,这个问题可以通过使用虚拟继承来解决,只是不是第一次描述的方式(即不是“public virtual Foo,public virtual PureVirtualBase”)。

FooPureVirtualDerivedBase都需要虚拟继承PureVirtualBase:

class PureVirtualBase {
public:
    virtual ~PureVirtualBase() {}
    virtual int IntFn() = 0;
};

class PureVirtualDerivedBase : public virtual PureVirtualBase {
public:
    virtual ~PureVirtualDerivedBase() {}
    virtual int OtherIntFn() = 0;
};

class Foo : public virtual PureVirtualBase {
public:
    virtual int IntFn() { return intVal; }
protected:
    int intVal;
};

class Bar : public Foo, public PureVirtualDerivedBase {
public
    virtual int OtherIntFn() { return 123; }
};

这样,Bar可以在Foo中添加OtherIntFn的实现,而不会丢失FooIntFn的实现。

Raviteja 是正确的,这是菱形问题的变体。但是,我们需要 Bar 继承自 PureVirtualDerivedBase 才能添加 OtherIntFn.

的附加功能

我同意 Revelnaut 的观点,尽量避免这种事情是好的,但在某些情况下这样做会很尴尬/不方便,尤其是当一个接口显然是另一个接口的子集时。

这是一个更具体的例子:

class IList {
public:
    virtual ~IList() {}
    virtual int GetLength() = 0;
};

class IDynamicList : public virtual IList {
    virtual ~IDynamicList() {}
    virtual int GetCapacity() = 0;
};

class ListOfFoo : public virtual IList {
public:
    virtual ~ListOfFoo() {}
    virtual int GetLength() { return length; }
    void UsefulFooFunction();

protected:
    int length;
};

class DynamicFooList : public ListOfFoo, public IDynamicList {
public:
    virtual ~DynamicFooList() {}
    virtual int GetCapacity() { return capacity; }
    void FooSpecificFnThatGrowsList();

protected:
    int capacity;
};

在这种情况下,IList 的功能是 IDynamicList 功能的一个子集。

您可以将接口分开,但是指向 IDynamicList 的指针将无法告诉您列表的长度;它只能告诉你容量。在这种情况下,当 IDynamicList 显然已经是一个列表时,传递/检索额外的指针以获取 IList 提供的功能将很麻烦。