C++ 在c++,为什么我们需要从子类调用grand_父构造函数?

C++ 在c++,为什么我们需要从子类调用grand_父构造函数?,c++,inheritance,constructor,diamond-problem,C++,Inheritance,Constructor,Diamond Problem,请阅读代码以了解情况 #include <iostream> using namespace std; class one { protected: int x; public: one(int a) { x=a; cout << "one cons called\n"; } void display(void) { cout << "x = " <<

请阅读代码以了解情况

#include <iostream>
using namespace std;
class one
{
protected:
    int x;
public:
    one(int a)
    {
        x=a;
        cout << "one cons called\n";
    }
    void display(void)
    {
        cout << "x = " << x << endl;
    }
    ~one()
    {
        cout << "one destroy\n";
    }
};
class two : virtual protected one
{
protected:
    int y;
public:
    two(int a,int b) : one(a),y(b)
    {
        cout << "two cons called\n";
    }
    void display(void)
    {
        one::display();
        cout << "y = " << y << endl;
    }
    ~two()
    {
        cout << "two destroy\n";
    }
};

class three : protected virtual one
{
protected:
    int z;
public:
    three(int a,int b) : one(a),z(b)
    {
        cout << "Three cons called\n";
    }
    void display(void)
    {
        one::display();
        cout << "z = " << z << endl;
    }
    ~three()
    {
        cout << "three destroy\n";
    }
};

class four : private two, private three
{
public:
    four(int a,int b,int c) :one(a), two(a,b),three(a,c)
    {
        cout << " four cons called\n";
    }
    void display(void)
    {
        one::display();
        cout << "y = " << y << endl;
        cout << "z = " << z << endl;
    }
    ~four()
    {
        cout << "four destroy\n";
    }
};
int main()
{
    four ob(1,2,3);
    ob.display();
    return 0;
}

错误消息如下:在我的代码块ide中没有调用“one::one()”的匹配函数

正如您所看到的,这是一个基于菱形问题的代码,其中类1是grand_父类。二、三班为家长班,四班为儿童班。所以我使用虚拟关键词来避免歧义。这里我理解的一切,除非有一件事。我知道当父类具有参数化构造函数时,我们需要从派生类向该构造函数提供参数。那么,为什么需要向构造函数1提供参数,其中类4只有两个父类,即2和3。
如果我不从类4调用构造函数1,代码将给我编译时错误。请解释我们为什么需要这样做。

层次结构中的
虚拟继承通过确保
one
的子类中只存储
one
的一个实例来消除基类
one
存在的歧义。回想一下,当继承某个类时,派生实例总是将一个基实例存储在内部的某个地方-因此
virtual
继承确保
one
two
three
中的实例在某种程度上被继承层次结构下的任何类“覆盖”

现在的问题是:谁负责初始化这一个
实例?应该是两个
还是三个
?显然不是两个都有,因为只有一个例子。这里是:它总是最派生的类,负责初始化
one
——这很有意义:嵌入基类副本的实例必须初始化它

这就是嵌入基类实例的类层次结构在没有
four
four
虚拟继承的情况下的外观:

              +----------+                           +----------+
              |   one    |                           |   one    |
              +----+-----+                           +----+-----+
                   |                                      |
                   |                                      |
         +-------+-----------+           virtual +--------+--------+ virtual
         |                   |                   |                 |
         |                   |                   |                 |
+--------+-------+   +-------+-------+      +----+----+       +----+----+
|      two       |   |      three    |      |  two    |       |  three  |
| +------------+ |   | +----------+  |      +----+----+       +----+----+
| |   one      | |   | |   one    |  |           |                 |
| +------------+ |   | +----------+  |           +--------+--------+
|  => must init! |   | => must init! |                    |
+----------------+   +---------------+            +-------+--------+
                                                  |     four       |
                                                  | +------------+ |
                                                  | |    one     | |
                                                  | +------------+ |
                                                  | => must init!  |
                                                  +----------------+

您可以这样来考虑这种机制:
virtual
继承赋予基类实例
virtual
-ness,这包括构造实例-这种责任向下传递到层次结构中。

当一个类的两个超类有一个公共基类时,菱形问题就会发生。 这个问题的解决方案是“Virtual”关键字。一般情况下,不允许直接调用祖父母的构造函数,必须通过父类调用。只有当我们使用“Virtual”关键字时才允许。
因此,当我们使用“virtual”关键字时,默认情况下会调用祖辈类的默认构造函数,即使父类显式调用参数化构造函数。

假设您有以下菱形:

     Base
    /    \
 Left    Right
    \    /
     Down
Base
类可以非常简单,它有一个由构造函数初始化的
int
成员:

struct Base
{
    Base(int x) 
        : x(x)
    {}
    virtual ~Base() = default;
    int x;
};
由于
Left
继承自
Base
,因此其构造函数可以将参数传递给
Base
构造函数。这里,如果您构造一个
对象,其
x
成员将是
1

struct Left : virtual Base
{
    Left() : Base(1)
    {}
};
另一个类,
Right
,也继承自
Base
。这意味着它的构造函数也可以将参数传递给
Base
构造函数。这里,它的
x
成员将是
2

struct Right : virtual Base
{
    Right() : Base(2)
    {}
};
现在有趣的部分来了:如果从
继承,会发生什么

// This does not compile.
struct Down : Left, Right
{
    Down() : Left(), Right()
    {}
};
Left
Right
都调用
Base
构造函数,但它们使用不同的参数。编译器现在应该使用
Left
中的
Base(1)
部分,还是应该使用
Right
中的
Base(2)
部分?答案很简单:两者都不用!编译器将选择权留给您,并允许您指定应使用哪个构造函数:

// Hooray, this version compiles.
struct Down : Left, Right
{
    Down() : Base(42), Left(), Right()
    {}
};

您显示的代码和类有一些可疑之处。例如,为什么要覆盖所有类中的非虚拟函数
display
?为什么你有
private
继承?通常建议不要使用私有继承来进行组合。@Someprogrammerdude。我写这段代码是为了学习,不是为了真正的项目写的。可能的重复:您是手工还是使用工具创建的图表?@0x499602D2初稿,然后在vim中进行一些视觉块编辑。
// This does not compile.
struct Down : Left, Right
{
    Down() : Left(), Right()
    {}
};
// Hooray, this version compiles.
struct Down : Left, Right
{
    Down() : Base(42), Left(), Right()
    {}
};