为什么不调用虚拟基础非默认构造函数,除非大多数派生基类显式调用它们?

时间:2017-06-28 13:33:57

标签: c++ constructor c++17 virtual-inheritance ctor-initializer

我想了解为什么C ++标准规定虚拟基础非默认构造函数不能被中间非最大派生的函数调用 class,如在此代码中,使用' -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;
}

因此,当编译WITHOUT -D_WITH_BUG_时,代码打印:

$ 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_编译时:

$ 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 ++标准要求它,但为什么呢?什么是理性?

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

3 个答案:

答案 0 :(得分:3)

拥有虚拟继承的全部目的是解决diamond problem。拥有虚拟基类后,您的层次结构如下所示:

  A
 / \
B   C
 \ /
  D

您需要知道何时才能构建A。您无法B构建它,然后C然后立即覆盖它 - 您需要将其构造一次。好的,那我们什么时候能做到呢?最简单的选择就是:让最派生的类做到这一点!因此,当我们初始化B的{​​{1}}子对象时,初始化其D子对象,因为A不是最多的衍生类型。

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

B

但是派生程度最高的类型A | B | C 必须初始化所有虚拟基础 - CAB无法初始化其B子对象的原因与复杂示例中的原因相同。

答案 1 :(得分:1)

此行为是因为virtual base class。由于A是虚拟基类,因此它由最派生的类构成 您可以查看diamond shape inheritance problemthis discussion on similar question,了解其必须采用的方式 首先要了解虚拟基类如何解决diamod形状问题 class A { ...}
class B: virtual public A {...}
class C: virtual public A {...}
class D: public B, public C {...}
当您将基类设为虚拟时,将有一个基类对象。中间派生类对象都将引用相同的单个基类对象。即这里如果创建了D的对象,那么B :: A和C :: A都将引用同一个对象。这个单个对象是B和C的基类。因此,如果允许通过中间类构造基类对象,则有两个派生类来构造这个单个对象。通过赋予派生类最多的构造虚拟基类的责任来解决这种歧义。

答案 2 :(得分:1)

您不太可能获得任何支持来更改语言。虚拟继承在多个继承方案中很有用。

  

为什么必须在这里忽略B(int a,int b)对A(a)的调用?

因为已经构造了唯一的A子对象。构造函数不是普通的函数,你不能在任何地方调用它。

你可以写

C(int a, int b, int c)
    : A(a), B(a, b), _c(c)
    { ... }

将为B::B(int, int)的正文提供传递给A::A(int)

的参数