调用虚基类的重载构造函数

时间:2010-08-16 16:22:50

标签: c++ constructor virtual-inheritance

是否有(实用的)绕过正常(虚拟)构造函数调用顺序的方法?

示例:

class A
{
    const int i;

public:
    A()
      : i(0)
    { cout << "calling A()" << endl; }

    A(int p)
      : i(p)
    { cout << "calling A(int)" << endl; }
};

class B
    : public virtual A
{
public:
    B(int i)
      : A(i)
    { cout << "calling B(int)" << endl; }
};

class C
    : public B
{
public:
    C(int i)
      : A(i), B(i)
    { cout << "calling C(int)" << endl; }
};

class D
    : public C
{
public:
    D(int i)
      : /*A(i), */ C(i)
    { cout << "calling D(int)" << endl; }
};


int main()
{
    D d(42);
    return 0;
}

输出:

  

致电A()
  叫B(int)
  调用C(int)
  调用D(int)

我想拥有的是:

  

调用A(int)
  叫B(int)
  调用C(int)
  调用D(int)


如您所见,涉及虚拟继承,这导致D的构造函数首先调用A的构造函数,但由于没有提供参数,因此它调用A()。需要初始化的是 const int i,所以我遇到了问题。

我想要隐藏C的继承细节,这就是为什么我在寻找一种方法来避免在D(和每个派生的)构造函数的初始化列表中调用A(i)。 [编辑]在这个特定的情况下,我可以假设只有非虚拟的单继承子类C(因为D是一个)。 [/编辑]

[编辑]

  

在初始化任何非虚拟基类之前初始化虚拟基类,因此只有最派生的类才能初始化虚拟基类。 - 詹姆斯麦克尼利斯

正是这一点,我希望最派生的类调用虚拟基类构造函数。 的 [/编辑]

请考虑以下情况(未在上面的代码示例中表示):

  A
 / \
B0  B1
 \ /
  C
  |
  D  

我理解为什么C在实例化C时必须调用A(歧义)的ctor,但为什么D在实例化D时必须调用它?

4 个答案:

答案 0 :(得分:7)

不幸的是,您将始终必须从派生程度最高的类调用虚拟基类构造函数。

这是因为你说的是​​虚拟基础在从它派生的所有类之间共享,用于对象的实例。由于构造函数只能在对象的给定实例中被调用一次,因此您必须在最派生类中显式调用构造函数,因为编译器不知道有多少类共享虚拟基础(从“The”中释放(可能很差)) C ++编程语言第3版,第15.2.4.1节)。这是因为编译器将从最基类的构造函数开始,并且工作到派生程度最高的类。直接从虚拟基类继承的类不会通过标准调用它们的虚基类构造函数,因此必须显式调用它。

答案 1 :(得分:2)

  

我理解为什么C必须打电话给   A(歧义)当你的时候   instanciate C,但为什么D必须   在实施D?

时调用它

出于同样的原因,C必须调用它。这不是歧义问题,而是A的构造必须只被调用一次(因为它是虚拟基础)。

如果您希望C能够初始化A的构造函数,那么如果D类继承C而另一个最终继承A的类会怎样?

答案 2 :(得分:0)

这是规则。存在用于覆盖虚拟功能的规则和用于构造虚拟基础子对象的规则。虽然两者在概念上非常相似,但它们遵循完全不同的规则,原因是:覆盖虚函数是显式的。默认构造函数隐式调用构造函数。

虚拟基类中的虚函数只需要有一个最终覆盖,覆盖所有其他覆盖的覆盖。 (非虚拟基类中的虚函数不可能有两个覆盖,使得一个不会覆盖另一个。)

但是虚拟基类构造函数总是从派生类最多的类中调用,并且通常以隐含的形式不打扰ctor-init-list中的虚拟基类,因为大多数类被设计为用作虚拟基础类是“纯接口”,没有数据成员,也没有用户初始化。

答案 3 :(得分:-1)

在parashift c ++ - faq-lite这个问题is outlined