6

在很多描述虚拟基类用法的教程中(通常用来解决菱形问题),往往会有类似这种结构设计的代码:

class Animal
{
public:
    Animal()
    {
        cout << "Creating Animal\n";
    }
};

///////////////////////////

class FourLegs : virtual public Animal
{
public:
    FourLegs()
    {
        cout << "Creating FourLegs\n";
    }
};

///////////////////////////

class Mammal : virtual public Animal
{
public:
    Mammal()
    {
        cout << "Creating Mammal\n";
    }
};

///////////////////////////

class Fox : public FourLegs, public Mammal
{
public:
    Fox()
    {
        cout << "Creating Fox\n";
    }
};

当我创建 Fox 的实例时,我得到了预期的输出,只创建了一个 Animal:

Creating Animal
Creating FourLegs
Creating Mammal
Creating Fox

如您所见,我虚拟继承了两个第 2 层类。现在,如果只有一个2 层类是虚拟继承的,而另一个是公开继承的,就会出现有趣的输出。例如,如果 FourLegs 是继承 public 而 Mammal 是继承 virtual public,则输出如下:

Creating Animal
Creating Animal
Creating FourLegs
Creating Mammal
Creating Fox

这很奇怪,并提出了一个问题:在继承树的某个地方创建一个涉及虚拟继承的类的完整过程是什么?

另一方面,如果我 FourLegs 如果继承了 virtual public,并且 Mammal 是继承了 public,那么输出是正常的(好像没有继承 virtual public):

Creating Animal
Creating FourLegs
Creating Animal
Creating Mammal
Creating Fox
4

2 回答 2

3

直接来自标准,12.6.2/10 [class.base.init]

在非委托构造函数中,初始化按以下顺序进行:

  • 首先,并且仅对于最派生类(1.8)的构造函数,虚拟基类按照它们在基类的有向无环图的深度优先从左到右遍历中出现的顺序进行初始化,其中“左- to-right” 是派生类base-specifier-list中基类的出现顺序。

  • 然后,直接基类按照它们出现在base-specifier-list中的声明顺序进行初始化(无论mem-initializers的顺序如何)。

  • 然后,非静态数据成员按照它们在类定义中声明的顺序进行初始化(同样不管mem-initializers的顺序)。

  • 最后,执行构造函数主体的复合语句

[注意:声明顺序是为了确保基子对象和成员子对象以初始化的相反顺序被销毁。——尾注]

第一个项目符号解释了如何对涉及虚拟继承的类进行初始化。

于 2012-06-24T21:41:06.637 回答
0

意外的输出并不意外。它发生是因为FourLegs派生自Animal并且必须调用 的构造函数Animalvirtual需要对所有中间类进行 -izing的既定惯例来防止该问题。您的示例的潜在问题是,的概念FourLegs被用作继承特征,而它应该被用作组合特征。也就是说,有一个字段描述哺乳动物/动物内部MammalAnimal(取决于具体要求)的腿数,并且派生类继承该字段。

于 2012-06-24T21:45:17.740 回答