【问题标题】:The process of creating a class that involves virtual inheritance创建涉及虚拟继承的类的过程
【发布时间】:2012-06-26 05:38:45
【问题描述】:

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

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 层类。现在,如果只有 一个 tier-2 类是虚拟继承的,而另一个是公开继承的,则可能会出现有趣的输出。例如,如果 FourLegs 是继承 public,而 Mammal 是继承 virtual public,则输出如下:

Creating Animal
Creating Animal
Creating FourLegs
Creating Mammal
Creating Fox

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

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

Creating Animal
Creating FourLegs
Creating Animal
Creating Mammal
Creating Fox

【问题讨论】:

  • 我确信这只是一个例子。然而,继承FourLegs 会很奇怪,因为狐狸四条腿。
  • 这些类的名称并不重要。我只是想了解更多关于虚拟继承的信息。
  • @JesseGood,也许将FourLegs 重命名为FourLegCreatures 可以解决问题。
  • 现在我知道该找谁来问我的班级应该命名什么了!
  • @ZERO:不只是名字,继承和组合都是很基础的概念,请googlewhat is the difference between inheritance and composition c++this question

标签: c++ inheritance diamond-problem


【解决方案1】:

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

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

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

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

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

  • 最后,构造函数体的compound-statement被执行。

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

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

【讨论】:

  • 首先,编译器转到 Fox 的构造函数并看到它继承自 FourLegs 和 Mammal?然后,它开始构造父类,从它的继承列表中的第一个直接父类开始(您在其中描述该类的继承方式等)。当它发现类被虚拟继承时会做什么?我想我在某处读过编译器首先构造虚拟继承的类。如果这是正确的,为什么会这样?这一步之后会发生什么?
  • @ZERO:您误解了报价。首先按照 DFS 顺序创建所有虚拟基,然后在构造所有虚拟基后,从左到右构造非虚拟基(base-specifier-list中基类的出现顺序) >) 您可能想检查这个较早的类似问题here
  • @David Rodríguez - dribeas:有趣。当我在另一个问题中查看您的答案时,我看到了具有深度优先从左到右搜索的创建列表,我认为这应该构造得非常好。为什么虚拟基类的顺序会发生变化?他们似乎有优先建造的优先权
  • @ZERO: 虚拟库(可能)由您的完整类型中的多个子对象共享​​>,但每个虚拟库的构造函数只能运行一次 i>,因此它们的处理方式必须与非虚拟基础不同(其中每个子对象按顺序构造它的所有基础)。解决这个问题可能有不同的方法,但标准中有一个简单的方法:只有最完整的类型才会调用虚基的构造函数,并且会按照预定义的顺序进行。
  • @David Rodríguez - dribeas:好点子。所以在构造了第一个虚拟基类之后,编译器继续到另一个类,并意识到那个类想要初始化那个虚拟基类的另一个实例,它会跳过吗?如果这是正确的,这意味着如果两个类从一个类(虚拟基类)虚拟继承,编译器得到的第一个将构造该虚拟基类。
【解决方案2】:

意外的输出并不意外。这是因为FourLegs 派生自Animal 并且必须调用Animal 的构造函数。 virtual-izing 所有中间类的既定约定是防止该问题的必要条件。您的示例的根本问题是 FourLegs 的概念被用作 inherited 特征,而 应该 被用作 compositional 特征.也就是说,MammalAnimal(取决于具体要求)中有一个字段描述哺乳动物/动物的腿数,派生类继承该字段。

【讨论】:

  • 四腿被用作遗传特征是什么意思?这和作曲特征有什么区别?
  • @ZERO 请参阅:Googlethis question
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-15
  • 2016-03-04
相关资源
最近更新 更多