【问题标题】:Diamond problem with only 1 virtual inheritance只有 1 个虚拟继承的菱形问题
【发布时间】:2014-10-23 18:34:35
【问题描述】:

这还能解决钻石问题吗?

class A
{};

class B : virtual A
{};

class C : A
{};

class D : B, C
{};

编辑:如果不是,那是什么?和这个一样吗?

class A
{};

class B : A
{};

class C : A
{};

class D : B, C
{};

还是别的什么?

【问题讨论】:

    标签: c++ virtual-inheritance diamond-problem


    【解决方案1】:

    钻石问题和对公共基础成员的模棱两可的调用可以通过下面的等效图形来最好地描述,它也提供了对内存模型的洞察

    示例 1

    class A
    {void foo(){};};
    
    class B :public A
    {};
    
    class C :public  A
    {};
    
    class D :public  B,public  C
    {};
    

    由于继承只是将两个对象的实现一个接一个地放在一起,因此基类 A 的所有方法都在派生类 B 和 C 中重复,其具有等价的图形表示

                              A              A
                              |              |
                              |              |
                               B             C
                                 \         /
                                    \    /
                                      D
    

    示例 2

    class A
    {};
    
    class B :public  virtual A
    {};
    
    class C :public  A
    {};
    
    class D :public  B,public  C
    {};
    

    使用virtual关键字,编译器为派生类B生成一个vtable,在B的vtable中带有一个vptr(虚拟指针),其中存储了基类A的偏移量。对于C,它仍然复制基类A的所有方法.所以类D,通过基类B通过vptr引用基类A的成员,其中类C引用基类A的成员作为副本。所以钻石问题和模糊性仍然存在。

                                   A         A
                                 /           |
                               /             |
                               B             C
                                 \         /
                                    \    /
                                      D
    

    示例 3

    class A
    {};
    
    class B :public  virtual A
    {};
    
    class C :public  virtual A
    {};
    
    class D :public  B,public  C
    {};
    

    现在,当派生类 B 和 C 都虚拟继承 A 时,Compiler 为 B 和 C 创建一个 Vtable,并在其每个 vtable 中创建一个 vptr 来引用基类 A 的偏移量。这最终解决了 Diamond 问题因为通过 B 或 C 只能看到一份副本

                                      A                                          
                                   /     \
                                 /         \                                
                               B             C
                                 \         /
                                    \    /
                                      D
    

    【讨论】:

      【解决方案2】:

      不,不会。 BC 都必须从 A 虚拟继承。

      【讨论】:

      • 请看我的问题的编辑。你也可以参考一下吗?
      【解决方案3】:

      如果从A类到B的继承标记为virtual但不是A类到C,那么C++将创建一个单独的虚拟A(D继承B,B继承A)和一个非虚拟A(D继承C,C继承A) )。因此,如果您想访问 A 的成员,您的以下代码将无法解决菱形问题。

      class A
      {};
      
      class B : virtual A
      {};
      
      class C : A
      {};
      
      class D : B, C
      {};
      

      和这个一样吗?

      class A
      {};
      
      class B : A
      {};
      
      class C : A
      {};
      
      class D : B, C
      {};
      

      上面的代码将为A的每个继承路径创建两个非虚拟A(D继承B,B继承A;D继承C,C继承A)。

      因此,两种情况的结果是相同的:钻石问题无法解决。

      最后,您应该为从 A 到 B 和从 A 到 C 的继承设置“虚拟”。在这种情况下,C++ 只会创建一个 A 对象。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-23
        • 1970-01-01
        • 2011-11-05
        • 1970-01-01
        • 2020-06-05
        相关资源
        最近更新 更多