几乎从纯虚拟(接口)类继承是一个很好的约定吗?

Gya*_*uyn 5 c++ multiple-inheritance virtual-inheritance pure-virtual

我经常使用纯虚拟类(接口)来减少当前项目中不同类的实现之间的依赖关系.我甚至拥有层次结构并不罕见,在这些层次结构中我有纯虚拟和非纯虚拟类来扩展其他纯虚拟类.以下是这种情况的一个例子:

class Engine
{ /* Declares pure virtual methods only */ }

class RunnableEngine : public virtual Engine
{ /* Defines some of the methods declared in Engine */ }

class RenderingEngine : public virtual Engine
{ /* Declares additional pure virtual methods only */ }

class SimpleOpenGLRenderingEngine : public RunnableEngine,
    public virtual RenderingEngine
{ /* Defines the methods declared in Engine and RenderingEngine (that are not
     already taken care of by RunnableEngine) */ }
Run Code Online (Sandbox Code Playgroud)

双方RunnableEngineRenderingEngine延长Engine几乎使钻石问题不会影响SimpleOpenGLRenderingEngine.

我想对钻石问题采取预防性的立场,而不是当它成为一个问题时处理它,特别是因为我喜欢编写尽可能容易让别人使用的代码,我不希望他们必须修改我的类,以便他们可以创建特定的类heirarchies,例如,如果Bob想要这样做:

class BobsRenderingEngine : public virtual RenderingEngine
{ /* Declares additional pure virtual methods only */ }

class BobsOpenGLRenderingEngine : public SimpleOpenGLRenderingEngine,
    public BobsRenderingEngine
{ /* Defines the methods declared in BobsRenderingEngine */ }
Run Code Online (Sandbox Code Playgroud)

如果我没有虚拟SimpleOpenGLRenderingEngine延伸,这是不可能的RenderingEngine.我知道Bob想要这样做的概率可能非常低.

因此,我已经开始使用永远扩展纯虚拟类的约定,以便从它们的多重继承不会导致钻石问题.也许这是因为我来自Java并且倾向于仅使用非纯虚拟类的单继承.我确信在某些情况下它可能有点过分但是使用这个约定有什么缺点吗?这会导致性能/功能等问题吗?如果不是,我没有看到不使用该惯例的理由,即使最终可能通常不需要.

J.N*_*.N. 3

我想说,一般来说,鼓励继承是一个坏主意(这就是您通过使用虚拟继承所做的事情)。实际上,很少有东西可以用继承隐含的树结构来很好地表示。此外,我发现多重继承往往会打破单一责任规则。我不知道您的确切用例,但我同意有时我们别无选择。

然而,在 C++ 中,我们有另一种方式来组合对象,即封装。我发现下面的声明更容易理解和操作:

class SimpleOpenGLRenderingEngine 
{
public:
    RunnableEngine& RunEngine() { return _runner; }
    RenderingEngine& Renderer() { return _renderer; }

    operator RunnableEngine&() { return _runner; }
    operator RenderingEngine&() { return _renderer; }

private:
    RunnableEngine _runner;
    RenderingEngine _renderer;
};
Run Code Online (Sandbox Code Playgroud)

确实,该对象将比虚拟继承使用更多的内存,但我怀疑是否会大量创建复杂的对象。

现在假设您确实想继承,因为某些外部约束。虚拟继承仍然很难操作:您可能对此感到满意,但从您的类派生的人可能不会,并且在派生之前可能不会考虑太多。我想更好的选择是使用私有继承。

class RunnableEngine : private Engine
{
     Engine& GetEngine() { return *this; }
     operator Engine&() { return *this; }
};

// Similar implementation for RenderingEngine

class SimpleOpenGLRenderingEngine : public RunnableEngine, public RenderingEngine
{ };
Run Code Online (Sandbox Code Playgroud)