简体   繁体   English

为什么派生类的构造函数要在C++中初始化虚拟基类?

[英]Why do the constructor of the derived classes want to initialize the virtual base class in C++?

My understanding, for instance reading this, is that the constructor of a derived class does not call its virtual base class' constructor.我的理解,例如阅读 本文,是派生类的构造函数不会调用其虚拟基类的构造函数。

Here is a simple example I made:这是我制作的一个简单示例:

class A {
    protected:
        A(int foo) {}
};

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

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

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


int main()
{
    return 0;
}

For some reason, the constructors B::B() and C::C() are trying to initialize A (which, again in my understanding, should have already been initialized by D at this point):出于某种原因,构造函数B::B()C::C()正在尝试初始化A (在我的理解中,此时应该已经由D初始化了):

$ g++ --version
g++ (GCC) 10.2.0
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ g++ test.cpp
test.cpp: In constructor ‘B::B()’:
test.cpp:8:13: error: no matching function for call to ‘A::A()’
    8 |         B() {}
      |             ^
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 {
      |       ^
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
test.cpp: In constructor ‘C::C()’:
test.cpp:13:13: error: no matching function for call to ‘A::A()’
   13 |         C() {}
      |             ^
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 {
      |       ^
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

I'm certain there is something very basic I misunderstood or am doing wrong, but I can't figure what.我确定我误解或做错了一些非常基本的事情,但我不知道是什么。

The constructor of virtual base is constructed.虚基构造函数构造。 It is constructed conditionally.它是有条件地构建的。 That is, the constructor of the most derived class calls the constructor of the virtual base.即最派生类的构造函数调用虚基类的构造函数。 If - this is the condition - the derived class with virtual base is not the concrete class of the constructed object, then it will not construct the virtual base because it has already been constructed by the concrete class.如果 - 这是条件 - 具有虚拟基类的派生类不是构造对象的具体类,那么它将不会构造虚拟基类,因为它已经被具体类构造了。 But otherwise it will construct the virtual base.但否则它将构建虚拟基地。

So, you must correctly initialise the virtual base class in constructors of all derived classes.因此,您必须在所有派生类的构造函数中正确初始化虚拟基类。 You simply must know that specific initialisation doesn't necessarily happen in case the concrete class is not the one which you are writing.您只需要知道,如果具体类不是您正在编写的类,则不一定会发生特定的初始化。 The compiler doesn't and cannot know whether you will ever create direct instances of those intermediate classes, so it cannot simply ignore their broken constructors.编译器不会也无法知道您是否会创建这些中间类的直接实例,因此它不能简单地忽略它们损坏的构造函数。

If you made those intermediate classes abstract, then the compiler would know that they are never the most concrete type and thus their constructor would not be required to initialise the virtual base.如果你使这些中间类抽象,那么编译器就会知道它们永远不是最具体的类型,因此它们的构造函数不需要初始化虚拟基类。

For some reason, the constructors B::B() and C::C() are trying to initialize A (which, again in my understanding, should have already been initialized by D at this point):出于某种原因,构造函数 B::B() 和 C::C() 正在尝试初始化 A (在我的理解中,此时应该已经由 D 初始化了):

But what should compiler do if somebody constructs C solo?但是如果有人单独构造 C,编译器应该怎么做? The final object D will call the constructor of A but you define constructor to C which implies that it can be constructed but the constructor is faulty cause it cannot construct A .最终对象D将调用A的构造函数,但您将构造函数定义为C ,这意味着它可以构造但构造函数错误,因为它无法构造A

Putting aside more complex class hierarchies, for any derived type there is exactly one copy of its virtual base.撇开更复杂的类层次结构不谈,对于任何派生类型,都只有其虚拟基类的一个副本。 The rule is that the constructor for the most-derived type constructs that base.规则是最派生类型的构造函数构造该基类。 The compiler has to generate code to handle the bookkeeping for that:编译器必须生成代码来处理簿记:

struct B { };
struct I1 : virtual B { };
struct I2 : virtual B { };
struct D : I1, I2 { };

B b;   // `B` constructor initializes `B`
I1 i1; // `I1` constructor initializes `B` subobject
I2 i2; // `I2` constructor initializes `B` subobject

So far, it's easy enough to picture, since the initialization is done the same way as it would be if B was not a virtual base.到目前为止,很容易想象,因为初始化的完成方式与B不是虚拟基的情况相同。

But then you do this:但是你这样做:

D d; // which constructor initializes `B` subobject?

If the base wasn't virtual, the I1 constructor would initialize its B subject, and the I2 constructor would initialize its B subobject.如果基不是虚拟的,则I1构造函数将初始化其B主体,而I2构造函数将初始化其B子对象。 But because it's virtual, there's only one B object.但是因为它是虚拟的,所以只有一个B对象。 So which constructor should initialize it?那么哪个构造函数应该初始化它呢? The language says that the D constructor is responsible for that.该语言说D构造函数对此负责。

And the next complication:下一个并发症:

struct D1 : D { };
D1 d1; // `D1` constructor initializes `B` subobject

So, along the way we've created five different objects, each with a virtual base of type B , and each with the B subobject being constructed from a different constructor.因此,一路上我们创建了五个不同的对象,每个对象都有一个B类型的虚拟基类,每个对象的B子对象都是从不同的构造函数构造的。

Putting the responsibility on the most-derived type makes the initialization easy to understand and to visualize.将责任放在派生最多的类型上使初始化易于理解和可视化。 There could have been other rules, but this one really is the simplest.可能还有其他规则,但这一条确实是最简单的。

暂无
暂无

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

相关问题 为C ++中的基类和派生类声明“虚拟”构造函数? - Declare 'virtual' constructor for base and derived class in c++? 您是否需要从所有派生类调用虚拟基类构造函数? 即使他们不是最衍生的? - Do you need to call virtual base class constructor from all derived classes? Even if they're not the most derived? C ++-派生类是否继承基类的静态成员? - C++ - Do derived classes inherit static members of the base class? 如何在C ++中的派生类的构造函数中初始化基类的const变量? - How can I initialize a const variable of a base class in a derived class' constructor in C++? C ++类-派生类中的构造函数声明 - C++ classes - constructor declaration in derived class 如果我想使用基类实现,为什么我必须在派生类中实现虚函数 - Why do I have to implement a virtual function in a derived class if I want to use the base class implementation 在C ++中,基类是否可以在所有派生类中拨入虚拟方法? - Any way in C++ for a base class to diallow virtual methods in all derived classes? 在C ++中派生类构造函数之后调用基类构造函数 - Call base class constructor after the derived class constructor in C++ c++ 中派生的 class 构造函数中的动态基 class 构造函数调用 - dynamic base class constructor call in derived class constructor in c++ C++派生类构造函数调用基类构造函数错误 - C++ derived class constructor call base class constructor errors
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM