C ++虚拟继承和构造函数

时间:2017-07-02 16:52:56

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

我有以下c ++代码(VS2013,如果重要的话):

#include <iostream>
using namespace std;

struct  A{
    A() { cout << "A()" << endl; }
    A(int i) { cout << "A(" << i << ")" << endl; }
};

struct B : /*virtual*/ public A{
    B(int i) : A(i){ cout << "B(" << i << ")" << endl; }
};

struct C : /*virtual*/ public A{
    C(int i) : A(i){ cout << "C(" << i << ")" << endl; }
};

struct D : /*virtual*/ public B, /*virtual*/ public C{
    D() : B(2), C(3){ cout << "D()" << endl; }
};

void main() {
    D d;
    system("pause");
}

该代码实现了菱形继承,总共有4个继承: B:A,C:A,D:B,D:C

如果没有将这些继承设置为virtual,我会得到以下输出:

  

A(2)B(2)A(3)C(3)D()

这是有道理的。 D()首先调用B(int x),它首先调用A(int x),然后调用C(int x),它首先调用A(int x),最后调用D()本身。

但就虚拟继承而言,我有4个继承,这意味着我总共有16个虚拟\非虚拟继承组合。

弄乱不同的选项会产生一些非常意外的结果,而且我对虚拟遗传的了解越多,我就越感到困惑。

以下是一些例子:

  1. 当只设置B:A到virtual时,我得到:
  2.   

    A()B(2)A(3)C(3)D()

    这是有道理的 - B虚拟地继承A,因此,除非另有说明,B(int x)调用A的默认构造函数。

    1. 当只设置C:A到virtual时,我得到:
    2.   

      A()A(2)B(2)C(3)D()

      为什么A的构造函数都在B和C之前?这种行为对我没有意义。

      1. 当只将D:C设置为virtual时,我得到:
      2.   

        A(3)C(3)A(2)B(2)D()

        为什么C的构造函数会在B之前?

        我可以继续下去。 其中一些组合会导致非常意外的结果(至少对我而言)。

        我知道虚拟继承的基础知识,并在简单的例子中理解它们,我已经看到很多关于它的问题。但其中一些行为仍然困扰着我。

        这些多个虚拟遗产是否有任何特定的规则?

        编辑: 我被重定向到这个问题: Mixing virtual and non-virtual inheritance of a base class 虽然这有很大帮助,但我仍然对在任何特定情况下调用哪个A的构造函数感到困惑。 我还需要一些帮助。

1 个答案:

答案 0 :(得分:2)

这里有两个大钥匙:

  1. 虚拟基类由最派生的类构造函数直接初始化,而不是由其他基类间接初始化。

  2. 虚拟基类在任何非虚拟基类之前初始化。

  3. 让我们看看你的例子。

      

    当只设置B:A到虚拟时我得到:

         

    A()B(2)A(3)C(3)D()

         

    这是有道理的 - B虚拟地继承A,因此,除非另有说明,B(int x)调用A的默认构造函数。

    B根本不会调用A的构造函数。 D直接为A的虚拟实例调用A。但由于D的构造函数未指定如何初始化A,因此您获得A的默认构造函数。

    A中的B(int i) : A(i)被忽略,不是因为A是虚拟基础,而是因为B不是派生最多的类,因此无法构建它的A

      

    当只设置C:A到虚拟时我得到:

         

    A()A(2)B(2)C(3)D()

         

    为什么A的构造函数都在B和C之前?这种行为对我没有意义。

    此处C::A是唯一的虚拟基类,因此首先进行初始化。同样,由于D的构造函数没有为A指定初始值设定项,因此该虚拟子对象使用默认构造函数。

    然后非虚拟基类按顺序排列。 B之前有C,但B首先需要初始化其非虚拟A2)。

      

    当只将D:C设置为虚拟时,我得到:

         

    A(3)C(3)A(2)B(2)D()

         

    为什么C的构造函数会在B之前?

    唯一的虚拟基类是C,因此它会在B之前构建。但是这次C的构造函数必须首先初始化其非虚拟基础子对象C::A。然后是B,必须首先初始化其非虚拟子对象B::A

    最后,存在“正常”模式,这使得A两个继承都是虚拟的,只产生一个A子对象,这是虚拟继承的整个点。使BC虚拟是毫无意义的,除非您希望D以更复杂的方式重用为基类。

    #include <iostream>
    using namespace std;
    
    struct  A{
        A() { cout << "A()" << endl; }
        A(int i) { cout << "A(" << i << ")" << endl; }
    };
    
    struct B : virtual public A{
        B(int i) : A(i){ cout << "B(" << i << ")" << endl; }
    };
    
    struct C : virtual public A{
        C(int i) : A(i){ cout << "C(" << i << ")" << endl; }
    };
    
    struct D : public B, public C{
        D() : A(1), B(2), C(3){ cout << "D()" << endl; }
    };
    
    void main() {
        D d;
        system("pause");
    }
    

    输出:

      

    A(1)B(2)C(3)D()

    注意我已向A(1)添加了初始化程序D,以表明虚拟子对象不一定需要使用默认构造函数。您可以在任何示例中完成此操作,其中至少有一个A继承是虚拟的。