创建涉及虚拟继承的类的过程

The process of creating a class that involves virtual inheritance

本文关键字:过程 继承 虚拟 创建      更新时间:2023-10-16

在许多描述虚拟基类用法的教程中(通常用于解决菱形问题),它们通常具有类似于此结构设计的代码:

class Animal
{
public:
    Animal()
    {
        cout << "Creating Animaln";
    }
};
///////////////////////////
class FourLegs : virtual public Animal
{
public:
    FourLegs()
    {
        cout << "Creating FourLegsn";
    }
};
///////////////////////////
class Mammal : virtual public Animal
{
public:
    Mammal()
    {
        cout << "Creating Mammaln";
    }
};
///////////////////////////
class Fox : public FourLegs, public Mammal
{
public:
    Fox()
    {
        cout << "Creating Foxn";
    }
};

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

Creating Animal
Creating FourLegs
Creating Mammal
Creating Fox

如您所见,我虚拟继承了两个 Tier-2 类。现在,如果只有一个 Tier-2 类是虚拟继承的,而另一个是公开继承的,则可能会出现有趣的输出。例如,如果 FourLegs 是继承的,而哺乳动物是继承的虚拟公共,则输出如下:

Creating Animal
Creating Animal
Creating FourLegs
Creating Mammal
Creating Fox

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

另一方面,如果 I FourLegs 继承了虚拟公共,而 Mammal 是继承了公共的,那么输出就像正常一样(好像没有任何东西被继承虚拟公共):

Creating Animal
Creating FourLegs
Creating Animal
Creating Mammal
Creating Fox

直接从标准 12.6.2/10 [class.base.init]

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

  • 首先,并且仅对于派生最多的类 (1.8) 的构造函数,虚拟基类按照它们在基类的有向无环图的深度优先从左到右遍历上的顺序进行初始化,其中"从左到右"是基类在派生类基说明符列表中的出现顺序。

  • 然后,直接基类按照它们出现在基说明符列表中的声明顺序进行初始化(无论 mem 初始值设定项的顺序如何)。

  • 然后,非静态数据成员按照它们在类定义中声明的顺序进行初始化(同样,无论 mem 初始值设定项的顺序如何)。

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

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

第一个项目符号说明如何使用涉及虚拟继承的类完成初始化。

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