【问题标题】:Class derived from a class with multiple inheritance in C++: the derived class is trying to call the root base class constructor从 C++ 中具有多重继承的类派生的类:派生类正在尝试调用根基类构造函数
【发布时间】:2020-11-09 16:44:58
【问题描述】:

如果这感觉像是 my last question. 的廉价续集,我很抱歉

我有一个菱形继承,其中D 派生自BC,而这两者又都(实际上)派生自AABC 是抽象的,多亏了我之前问题的答案,编译器现在知道了,一切都很好。

现在,我需要创建一个派生自 D 的类 E。据我所知,通常构造函数E::E 应该调用D::D,而调用所有A::AB::BC::C 将是D::D 的工作。

但我的编译器确实坚持让E::E 调用A::A 本身。

这是我做的一个简单的例子:

class A {                       //abstract
    protected:
        A(int foo) {}
        virtual void f() =0;
};

class B: public virtual A {     // abstract
    protected:
        B() {}
};

class C: public virtual A {     // abstract
    protected:
        C() {}
};

class D: public B, public C {   // concrete
    public:
        D(int foo, int bar) :A(foo) {}
        void f() {}
};

class E: public D {             // concrete
    public:
        E(int foo, int bar, int buz) :D(foo, bar) {}
};


int main()
{
    return 0;
}

这是编译错误:

$ g++ test.cpp
test.cpp: In constructor ‘E::E(int, int, int)’:
test.cpp:25:49: error: no matching function for call to ‘A::A()’
   25 |         E(int foo, int bar, int buz) :D(foo, bar) {}
      |                                                 ^
test.cpp:3:9: note: candidate: ‘A::A(int)’
    3 |         A(int foo) {}
      |         ^
test.cpp:3:9: note:   candidate expects 1 argument, 0 provided
test.cpp:1:7: note: candidate: ‘constexpr A::A(const A&)’
    1 | class A {                       //abstract
      |       ^
test.cpp:1:7: note:   candidate expects 1 argument, 0 provided
test.cpp:1:7: note: candidate: ‘constexpr A::A(A&&)’
test.cpp:1:7: note:   candidate expects 1 argument, 0 provided

我知道虚拟继承是正确的,并且我知道编译器知道我想要抽象哪些类以及我想要实例化哪些类,因为如果我删除 class E,代码就会编译。

我错过了什么?

【问题讨论】:

  • 编译器告诉你 A 的哪些构造函数可用。您可以看到您定义的 A(int),加上复制和移动构造函数。通过定义自己的 A 构造函数,您可以告诉编译器不要声明默认构造函数。查看示例en.cppreference.com/w/cpp/language/default_constructor

标签: c++ multiple-inheritance instantiation


【解决方案1】:

但我的编译器确实坚持让 E::E 自己调用 A::A。

就像我在回答你之前的问题时解释的那样:“最派生类的构造函数调用虚拟基的构造函数”

包含虚拟基的层次结构中的所有非抽象类都必须正确初始化虚拟基,因为它们可能被实例化为最派生的类。例如,如果你创建了一个E 的实例,那么它就是E 的构造函数初始化了虚拟基A

在您的代码中,E 的构造函数尝试通过省略初始化程序来使用 A 的默认构造函数。但是A 不是默认可构造的,所以程序格式不正确。

我错过了什么?

E 的构造函数中虚基A 的初始化器。

据我所知,没有办法将虚拟基础的构建委托给另一个具体基础,例如D

【讨论】:

  • 在另一个问题中是有意义的,例如B 被认为是可实例化的,它必须能够初始化 A。在当前示例中,我想不出E 需要直接初始化A 的场景。此外,如果我将E 的构造函数定义为E::E(...) :D(foo, bar), A(foo) {},那么D::D(...) :A(foo) 会发生什么?会有两个A 实例吗?
  • @scozy I cannot think of a scenario where it would be necessary for E directly to initialize A 不清楚你为什么这么认为。 E 有一个虚拟基础 A(基础是否直接无关)并且它是具体的,因此 E 必须直接初始化 A 的场景是 E 被实例化为最派生对象的场景。
  • @scozy what will happen with D::D(...) :A(foo)? 就像我在另一个答案中写的那样:“您必须知道,如果具体类不是您正在编写的类,则不一定会发生特定的初始化".应用于这种情况,由于在实例化 E 时 D 不是派生最多的类,因此 D 不会初始化 A。D 的构造函数根本不会初始化基 A,并且不会使用您为 D 编写的初始化程序。跨度>
猜你喜欢
  • 2015-11-03
  • 1970-01-01
  • 2011-01-08
  • 1970-01-01
  • 2021-04-20
  • 2011-05-03
  • 1970-01-01
  • 2015-08-18
  • 2016-07-19
相关资源
最近更新 更多