虚拟继承和可怕的钻石

Virtual Inheritance and dreaded diamond

本文关键字:钻石 继承 虚拟      更新时间:2023-10-16

我遇到了一个可怕的钻石问题。提醒一下,这里是这个问题的经典类层次结构:

    B
   / 
 C1   C2
    /
    D

为了解决这个问题,标准的解决方案是让C1和C2使用虚拟继承来继承B.

我的问题是B和C1来自一个我无法修改的SDK下面的示例中,我无法使SubClassBBase虚拟继承。类:PureVirtualBase、Base和SubClassB来自我使用的SDK。我不能修改它们。SubClassA和Leaf是我的自定义类。我可以改变它们。

     PureVirtualBase(SDK)
           |
        Base(SDK)
       /        
 SubClassA   SubClassB(SDK)
               /
          Leaf

SubClassB无法更改为使用Base的虚拟继承的情况下。如何应该这样:

  • 实例只包含一个
  • 尝试访问在PureVirtualBase中定义的纯虚拟函数和在Base
class PureVirtualBase
{
public:
    PureVirtualBase()
    {
        cout<<"<<PureVirtualBase::PureVirtualBase" << endl;
        cout<<">>PureVirtualBase::PureVirtualBase" << endl;
    }
    virtual int f_PureVirtualBase()=0;
};
class Base : public PureVirtualBase
{
public:
    Base(std::string id) {
        cout<<"<<Base::Base:"<<id << endl;
        m_id=id;
        cout<<">>Base::Base:"<<m_id << endl;
    }
    virtual int f_PureVirtualBase() {
       cout<<"Base::f_PureVirtualBase" << endl;
       return 1;
    }
private:
    std::string m_id;
};
class SubClassA:  public virtual Base
{
public:
    SubClassA(): Base("From SubClassA") {
        cout<<"<<SubClassA::SubClassA" << endl;
        cout<<">>SubClassA::SubClassA" << endl;
    }
};
class SubClassB:  public Base
{
public:
    SubClassB():Base("From SubClassB") {
        cout<<"<<SubClassB::SubClassB" << endl;
        cout<<">>SubClassB::SubClassB" << endl;
    }
};    
class Leaf:  public SubClassA, public SubClassB
{
public:
    Leaf():SubClassA(),  SubClassB(), Base("From Leaf") {
        cout << "<<Leaf::Leaf" << endl;
        cout << ">>Leaf::Leaf"<< endl;
    }
};
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    Leaf myleaf;
    myleaf.f_PureVirtualBase();
    return a.exec();
}
  • 如果我对f_PurevirtualBase的调用进行注释,它会编译,但我有一个警告由于歧义,虚拟基"base"在"Leaf"中不可访问如果我取消注释此调用:我会收到此错误:请求成员"f_PureVirtualBase"不明确
  • 如果我在这个调用前面加上类名(myleaf.SubClassA::f_PureVirtualBase()那么它是有效的,但有些东西显然是错误的,因为中包含2个对象)

有什么提示吗?

回答评论的更多信息

我的目标体系结构比我在最初的问题中提供的示例稍微复杂一些:

 PureVirtualBase(SDK)
       |
     Base(SDK)
        |
        --SubClassA
        --SubClassB(SDK)
        --SubClassC(SDK)
        --SubClassD(SDK)

LeafOne:继承自SubClassA和SubClassB(SDK)

LeafTwo:继承自SubClassA和SubClassC(SDK)

LeafThree:继承自SubClassA和SubClassD(SDK)

SubClassA是我自己的私有代码。它提供自定义功能。SDK方法应该能够将其视为一个Base实例这个类不会被实例化,但它在这里可以在执行某些处理时同时处理LeafOne、LeafTwo和LeafThree。

这表明您的设计有问题,对此最简单的答案是首先避免菱形。您为示例代码选择的名称太糟糕了,很难推断您实际想要做什么,但无论如何,请重新考虑您是否需要从父母双方继承,以及这是否有意义。

继承是OO语言中最受滥用的构造之一,它确实解决了一个问题,但在其他任何地方都被用作金锤。很多时候,你手里拿的是螺丝钉,而不是钉子,正确的工具也不是锤子。

如果你真的被这些设计约束所困扰,我肯定会直接从B中用一个将C1和C2作为复合组件的类进行子类化。不幸的是,这需要手动镜像他们的接口(希望它很小,或者你可以将其限制在你需要的范围内),并代理到子组件。它并不漂亮,但除非你能在其他地方对设计做出一些让步,否则你就没有太多选择了。

当然,一个缺点是你没有你想要的类型标识(子类不满足C1或C2的"isa"),这可能足以让这种方法失效。

它不漂亮。但我认为,考虑到您的限制,这可能是"最不坏"的解决方案。