为什么除非大多数派生基显式调用它们,否则不调用虚拟基非默认构造函数? [英] why are virtual base non-default constructors not called unless most-derived base explicitly invokes them?

查看:140
本文介绍了为什么除非大多数派生基显式调用它们,否则不调用虚拟基非默认构造函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想了解WHY C ++标准的要求,即虚拟基础非默认构造函数不能由非最衍生的中间层调用 此类代码中的类,当使用'-D_WITH_BUG_'编译时:

I would like to understand WHY C++ standard mandates that virtual base non-default constructors cannot be invoked by an intermediate NOT most-derived class, as in this code, when compiled with '-D_WITH_BUG_' :

/*  A virtual base's non-default constructor is NOT called UNLESS 
 *  the MOST DERIVED class explicitly invokes it
 */

#include <type_traits>
#include <string>
#include <iostream>

class A
{
public:
    int _a;
    A():  _a(1)
    {
        std::cerr << "A() - me: " << ((void*)this) << std::endl;
    }
    A(int a): _a(a)
    {
        std::cerr << "A(a) - me:" << ((void*)this) << std::endl;
    }
    virtual ~A()
    {
        std::cerr << "~A" << ((void*)this) << std::endl;
    }
};

class B: public virtual A
{
public:
    int _b;
    B(): A(), _b(2)
    {
        std::cerr << "B() - me: " << ((void*)this) << std::endl;
    }
    B(int b) : A(), _b(b)
    {
        std::cerr << "B(b) - me: " << ((void*)this) << std::endl;
    }
    B(int a, int b): A(a), _b(b)
    {
        std::cerr << "B(a,b) - me: " << ((void*)this) << std::endl;
    }
    virtual ~B()
    {
        std::cerr << "~B" << ((void*)this) << std::endl;
    }
};

class C: public virtual B
{
public:
    int _c;
    C(): B(), _c(3)
    {
        std::cerr  << "C()" << std::endl;
    }
    C(int a, int b, int c)
    :
#ifdef _WITH_BUG_    
    B(a,b)
#else
    A(a), B(b)
#endif    
    , _c(c)
    {
        std::cerr  << "C(a,b) - me: " << ((void*)this) << std::endl;    
    }
    virtual ~C()
    {
        std::cerr << "~C" << ((void*)this) << std::endl;
    }  
};
extern "C"
int main(int argc, const char *const* argv, const char *const* envp)
{
    C c(4,5,6);
    std::cerr << " a: " << c._a  << " b: " << c._b << " c: " << c._c 
              <<  std::endl;
    return 0;
}

因此,当不使用-D_WITH_BUG_进行编译时,代码将输出:

So, when compiled WITHOUT -D_WITH_BUG_, the code prints:

$ g++ -I. -std=gnu++17 -mtune=native -g3 -fPIC -pipe -Wall -Wextra \
  -Wno-unused -fno-pretty-templates -Wno-register  \
  tCXX_VB.C -o tCXX_VB 
$ ./tCXX_VB
A(a) - me:0x7ffc410b8c10
B(b) - me: 0x7ffc410b8c00
C(a,b) - me: 0x7ffc410b8bf0
a: 4 b: 5 c: 6
~C0x7ffc410b8bf0
~B0x7ffc410b8c00
~A0x7ffc410b8c10

但是当使用-D_WITH_BUG_编译时:

But when compiled with -D_WITH_BUG_ :

$ g++ -I. -std=gnu++17 -mtune=native -g3 -fPIC -pipe -Wall -Wextra \ 
  -Wno-unused -fno-pretty-templates -Wno-register \
  -D_WITH_BUG_ tCXX_VB.C -o tCXX_VB
$ ./tCXX_VB
A() - me: 0x7ffd7153cb60
B(a,b) - me: 0x7ffd7153cb50
C(a,b) - me: 0x7ffd7153cb40
a: 1 b: 5 c: 6
~C0x7ffd7153cb40
~B0x7ffd7153cb50
~A0x7ffd7153cb60

为什么在这里必须忽略B(int a,int b)对A(a)的调用? 我了解C ++标准的要求,但是为什么呢?有理是什么?

Why must B(int a, int b)'s invocation of A(a) be ignored here ? I understand the C++ standard mandates it, but why ? What is the rational ?

如果我仅实例化一个B对象: B b(4,5); 确实得到正确的b._a值为4;但是如果B是C的子类: C c(4,5,6) C :: a最终为1,IFF c不直接调用A(a). 因此,如果B(a,b)是子类对象,则其值是不同的 比它是一个最派生的对象. 这对我来说是非常混乱和错误的.有没有希望 足够多的人同意在此基础上更改C ++标准?

If I instantiate just a B object : B b(4,5) ; this DOES get a correct b._a value of 4; but if B is a subclass of C: C c(4,5,6) C::a ends up being 1, IFF c does not DIRECTLY INVOKE A(a) . So the value of a B(a,b) is different if it is a subclass object than if it is a most-derived object . This to me is very confusing and wrong. Is there any hope of getting enough people to agree to change the C++ standard on this ?

推荐答案

拥有虚拟继承的全部目的是解决钻石问题.一旦有了虚拟基类,您的层次结构将如下所示:

The entire purpose of having virtual inheritance is to solve the diamond problem. Once you have a virtual base class, and your hierarchy looks like this:

  A
 / \
B   C
 \ /
  D

您需要知道何时构造A.您不能先B构造它,然后C然后立即覆盖它-您需要将它构造一次.好的,那我们什么时候可以做?最简单的选择就是:让最派生的类来做!因此,当我们初始化DB子对象时,它不会初始化其A子对象,因为B不是最派生的类型.

You need to know when to construct the A. You can't have B construct it and then C then immediately overwrite it - you need it to be constructed exactly once. Okay, so when can we do that? The simplest choice is just: make the most derived class do it! So when we're initializing the B subobject of D, it will not initialize its A subobject because B is not the most derived type.

在您的情况下,您的层次结构仍然是线性的:

In your case, your hierarchy is still linear:

A
|
B
|
C

,但最派生的类型C必须初始化所有虚拟基数-AB. B不会出于复杂示例中未初始化的原因而初始化其A子对象.

but the most derived type, C, has to initialize all the virtual bases - A and B. B won't initialize its A subobject for the same reason as it didn't in the complicated example.

这篇关于为什么除非大多数派生基显式调用它们,否则不调用虚拟基非默认构造函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆