抽象类的虚基类的初始化

时间:2014-08-20 05:37:03

标签: c++ constructor abstract-class

考虑代码:

#include <iostream>

using std::cout;
using std::endl;

struct A
{
    virtual void foo(){ };

    A()
    {
        cout << "A()" << endl;
    }
};

struct B : virtual A
{
    virtual void bar() = 0;

    B() : A() //mem-initializer of virtual base class
    {
        cout << "B()" << endl;
    }
};

struct C : B
{
    void bar(){ };
    C() : B()
    {
        cout << "C()" << endl;
    }
};

C c;

int main()
{
    //A()
    //B()
    //C()
    //Is output
}

demo

我已编写代码以了解 12.6.2 / 8 N37973 中的规则说明:

  

[注意:抽象类(10.4)永远不是最派生的类,因此   它的构造函数永远不会初始化虚拟基类,因此   可以省略相应的mem初始化器。 - 结束说明]

如果我们在B()中省略mem-initializer并将基类A设为非虚拟,我们将得到相同的结果。

Actually

那么我引用的那个注释的目的是什么?

3 个答案:

答案 0 :(得分:3)

它在谈论这样的事情:

#include <iostream>

using std::cout;
using std::endl;

struct A
{
    virtual void foo(){ };

    // N.B.: no default ctor
    A(int)
    {
        cout << "A(int)" << endl;
    }
};

struct B : virtual A
{
    virtual void bar() = 0;

    B()
    {
        cout << "B()" << endl;
    }
};

struct C : B
{
    void bar(){ };
    C() : A(10), B()
    {
        cout << "C()" << endl;
    }
};

C c;

int main()
{

}

如果A不是B的虚拟基础,或B不是抽象的,那么B::B()必须有一个合适的 mem-initializer < / em> for A;但是,由于B是抽象的,A是虚拟基础,而B的构造函数永远不会构造A,因此 mem-initializer 可以省略。请注意,g ++目前没有实现此规则,仍然需要B mem-initializer (它永远不会实际使用)。然而,Clang确实如此。 Demo

另请参阅引入此规则的CWG issue 257,后面的CWG issue 1658进一步修改了措辞。相关GCC错误报告为bug 1924953878

答案 1 :(得分:2)

如果BA继承非虚拟,那么B的构造函数必须初始化A基类子对象。由于A有默认构造函数,因此您不必在 ctor-initializer 中明确提及A B。但是,如果A没有默认构造函数,BA继承非虚拟,则必须在 ctor-中显式初始化A B的初始化程序

如果B实际上从A继承,而B是一个抽象类,那么即使A没有默认构造函数,您仍然不需要在{em> ctor-initializer 中提及A的{​​{1}}。这是因为B的构造函数永远不会负责初始化B子对象;相反,派生程度最高的类的构造函数是必须初始化A子对象的构造函数,因为A是一个虚拟基类。

答案 2 :(得分:1)

您的示例没有太多证明,因为虚拟基础A具有默认构造函数。您永远不必为虚拟基类(或任何基类)显式调用默认构造函数。编译器将在需要的任何地方隐式调用该默认构造函数,无论您是否自己明确地完成了它。

删除默认构造函数,并在A

中提供带参数的构造函数
struct A
{
    A(int)
    {
        cout << "A()" << endl;
    }
};

有问题的说明告诉您,现在您必须从A(直接来自A的每个非抽象类中显式调用基础A的构造函数或间接)。在您的情况下,您必须从C

的构造函数中显式初始化struct C : B { void bar(){ } C() : A(42) { cout << "C()" << endl; } };
A

因为C不是抽象类。

但是您不必从B的构造函数初始化B,因为struct B : virtual A { virtual void bar() = 0; B() { cout << "B()" << endl; } }; 是一个抽象类。即此

A

应该在C ++ 11中编译,即使是“经典”(pre-C ++ 11)语言规则也需要从B的构造函数中明确初始化B。< / p>

请注意,即使在C ++ 11模式下,ideone使用的GCC编译器(例如)仍会报告类{{1}}的上述定义的错误。显然,它还没有更新到遵循C ++ 11的规则。