繁体   English   中英

C ++中的虚拟继承被误解

[英]virtual inheritance in c++ misunderstood

我读到在虚拟继承中,构造函数被称为“从最派生的”。 考虑下面的代码。 以我的观点,这里派生最多的类是D。然后是B和C,“派生最多的”是A。那么,为什么首先调用最“ Base”的构造函数而不是“最派生”? 谢谢。

#include <iostream>
using namespace std;

struct A
{
    A()
    {
        cout << "A default constructor" << endl;
    }
};

struct B : virtual public A
{
    B() : A()
    {
        cout << "B default constructor" << endl;
    }
};

struct C : virtual public A
{
    C() : A()
    {
        cout << "C default constructor" << endl;
    }
};

struct D : public B, public C
{
    D() : B(), C()
    {
        cout << "D default constructor" << endl;
    }
};

int main()
{
    D d;
}

这是输出:

A default constructor
B default constructor
C default constructor
D default constructor

更新:


好。 因此请考虑以下代码。 请注意,10是印虽然d,B和C的构造发送7。这里实际上基类被称为第一个。 从D到B再到A没有链。首先调用A()(实际上是默认构造函数)。 然后才调用B和C构造函数。

但是我读到:“从最衍生的。” 来源: c ++虚拟继承

这里最派生的是D,然后是B和C,然后才是A。那么,为什么不考虑从构造函数传递给它的参数B,D,C首先调用A呢? 谢谢。

编码 :

#include <iostream>
using namespace std;

struct A
{
    int _x;
    A()
    {
        _x = 10;
        cout << "A default constructor" << endl;
    }
    A(int x)
    {
        _x = x;
        cout << "A NOT-default constructor" << endl;
    }
};

struct B : virtual public A
{
    B(int x=7) : A(x)
    {
        cout << "B  constructor" << endl;
    }
};

struct C : virtual public A
{
    C(int x=7) : A(x)
    {
        cout << "C  constructor" << endl;
    }
};

struct D : public B, public C
{
    D(int x=7) : B(x), C(x)
    {
        cout << "D  constructor" << endl;
    }
};

int main()
{
    D d;
    cout << d._x;
}

输出 :

A default constructor
B  constructor
C  constructor
D  constructor
10

在C ++中,构造顺序非常简单:

  1. 您调用了派生最多的ctor(通过初始化变量; 自动存储类static ,动态等)。
  2. 该ctor初始化所有子对象:
    • 委托的ctor称为另一个最派生的ctor。
    • 一个非授权的ctor自己完成工作:
      1. 如果是最衍生的ctor,则virtual基数按从左到右的声明顺序排列。 (这意味着将忽略非最基本派生给定的虚拟基ctor的参数)
      2. 其他直接基准按从左到右的声明顺序排列。
      3. 成员按声明顺序排列。
  3. ctor主体运行。

由于最基础的ctor是唯一的虚拟基础,因此它的主体是第一个运行的ctor主体,直接由最衍生的ctor调用。

通常,调用virtual函数, typeiddynamic_cast是安全的,尽管在全部初始化基本子对象之前不是这样: 我可以调用虚拟函数来初始化基本类子对象吗?

这意味着初始化派生的任何虚拟基础子对象及其直接派生的对象是大多数派生类的责任。 也就是说,所有基本构造函数都从派生最广的构造函数调用: D的构造函数调用A的构造函数,然后调用BC ,最后进行自身初始化。 必须确保共享基础对象仅初始化一次,并且在派生自该基础对象的任何类之前初始化。

这并不意味着顺序是从最大到最小。 与常规继承一样,基本子对象始终首先被初始化,因此在初始化派生类时可用。

为了回答您的更新问题,由于D初始化A ,它将调用默认构造函数,除非其初始化列表包含A的条目:

D(int x=7) : B(x), C(x)        // calls A(), initialising with 10
D(int x=7) : A(x), B(x), C(x)  // calls A(int), initialising with 7

B (或C )的初始化程序列表中A任何条目仅在B (或C )是派生程度最高的类时使用(因此负责初始化A )。

如果派生类的构造函数代码在构造基类之前运行您如何建议其行为? 尽管在技术上可行,但这完全没有用。

您观察到的行为是唯一理智的行为,与虚拟继承无关。

你说

我读到在虚拟继承中,构造函数被称为“从最派生的”。

那是真实的。 让我详细说明。

就您而言, D是最派生的。

当你构建的一个实例D ,对于构造A是从构造称为D因为是唯一的实例A为每个实例D A的构造函数不会从BC的构造函数中调用。

当构造B的实例时,会从B的构造函数中调用A的构造函数。 对于C的实例类似。

如果您有D的子类型,

struct E : public D
{
};

并且创建E的实例,然后从E的构造函数调用A的构造函数。

C ++草案标准(N3337)对涉及虚拟基类的初始化进行了说明:

12.6.2初始化基础和成员

5初始化应按以下顺序进行:

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

这就是对象的构建方式。

更新

看看这个例子:

class A{
    A()
    {
    cout << "Constructor A"<<endl;
    }

    ~A()
    {
    cout << "Destructor A"<<endl;
    }
}

class B : public A{
    B()
    {
    cout << "Constructor B"<<endl;
    }

    ~B()
    {
    cout << "Destructor B"<<endl;
    }
}
class C : public B{
    C()
    {
    cout << "Constructor C"<<endl;
    }

    ~C()
    {
    cout << "Destructor C"<<endl;
    }
}

创建一个类C的对象:

C obj;

输出将如下所示:

构造函数A
构造器B
构造函数C
析构函数C
析构函数B
析构函数A

执行的原因是这样的:

当一个类从另一个类派生时,它派生该类的属性。 派生类的功能可能取决于或可能不取决于基类的功能,但决不能相反。 假定派生类取决于基类功能,重要的是在初始化派生类之前正确初始化基类。

更新:

当创建C对象时,在C的构造函数可以执行之前,他从C构造函数获得的控制权将转移到它的基类的构造函数中。 这就是我首先提到的基类。

更新:

通过绘制对象关系可以最好地回答您的问题。
我们将在顶部具有A ,在底部具有D

“在虚拟继承中,[虚拟基础]构造函数是从最衍生的” [类型的构造函数]中调用的。

通过以上声明,他们要求您从派生最多的类型的构造函数( D )开始,一直延伸到最基类的( A )构造函数。

更新:

@leemes在注释中使执行流程更清晰:

构造函数本身“重定向”到基础的构造函数。 返回之后,它将继续使用自己的构造函数。 您错过的是用花括号写的内容不是整个实现。 这只是在调用基本ctor并初始化成员变量之后发生的事情。 与ctor相同:在花括号中,先编写要执行的内容,然后再调用成员变量的ctor,然后再调用基数的ctor。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM