首页 > 解决方案 > 在 c++ 中的菱形问题中,为什么我们需要从子类中调用 grand_parent 构造函数?

问题描述

请阅读代码以了解情况。

#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;
}

如果我替换代码

four(int a,int b,int c) :one(a), two(a,b),three(a,c)

four(int a,int b,int c) :two(a,b),three(a,c)

一个错误消息,例如:在我的代码块 ide 中没有调用 'one::one()' 的匹配函数。

如您所见,这是基于菱形问题的代码。其中第一类是祖父类。二级和三级作为父级,四级作为子级。所以我使用 virtual 关键字来避免歧义。我在这里理解的一切,除非一件事。我知道当父类具有参数化构造函数时,我们需要从派生类向该构造函数提供参数。那么为什么需要向构造函数一提供参数,其中类 4 只有 2 个父类,即 2 和 3 。如果我不调用第四类中的构造函数一,代码会给我编译时错误。请解释一下为什么我们需要这样做。

标签: c++inheritanceconstructordiamond-problem

解决方案


层次结构中的virtual继承one通过确保只有一个实例one存储在twoor的子类中来消除基类的存在歧义three。回想一下,当继承某个类时,派生实例将始终在内部存储基实例 - 因此virtual继承确保oneinside of的实例twothree在继承层次结构中被任何类“覆盖”。

现在的问题是:谁负责初始化这一个one实例?应该是two还是three?显然不是他们两个,因为只有一个实例。你在这里:它总是负责初始化的最派生one类- 这是有道理的:嵌入基类副本的实例必须初始化它。

four这就是带有嵌入式基类实例的类层次结构在没有和有fourvirtual继承的情况下的样子:

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

你可以这样想这种机制:virtual继承赋予基类实例virtual-ness,这包括构造实例-这种责任被传递到层次结构中。


推荐阅读