C++ 构造函数调用层次结构

C++ 构造函数调用层次结构,c++,constructor,call,hierarchy,C++,Constructor,Call,Hierarchy,我在类型层次结构中调用构造函数的规则时遇到了麻烦。以下是我的工作: class A{ protected: int _i; public: A(){i = 0;} A(int i) : _i(i){} virtual ~A(){} virtual void print(){std::cout<<i<<std::endl;} }; class B : virtual public A{ protected: int _j;

我在类型层次结构中调用构造函数的规则时遇到了麻烦。以下是我的工作:

class A{
protected:
    int _i;
public:
    A(){i = 0;}
    A(int i) : _i(i){}
    virtual ~A(){}
    virtual void print(){std::cout<<i<<std::endl;}
};

class B : virtual public A{
protected:
    int _j;
public:
    B() : A(){_j = 0;}
    B(int i, int j) : A(i), _j(j){}
    virtual ~B(){}
    virtual void print(){std::cout<<i<<", "<<j<<std::endl;}
};

class C : virtual public B{
protected:
    int _k;
public:
    C() : B(){_k = 0;}
    C(int i, int j, int k} : B(i,j), _k(k){}
    virtual ~C(){}
    virtual void print(){std::cout<<i<<", "<<j<<", "<<k<<std::endl;}
};

int main(){
    C* myC = new C(1,2,3);
    myC->print();
    delete myC;
    return 0;
}
A类{
受保护的:
int_i;
公众:
A(){i=0;}
A(int i):_i(i){}
虚拟~A(){}

virtual void print(){std::cout这是因为您使用了虚拟继承,这仅在存在多个继承的情况下才有意义。只需正常继承,一切都将如您所愿。

您真的需要
虚拟继承吗?
您遇到了一个问题,因为第一个虚拟基ctor将首先被调用,但当从
B
继承
C
时,您没有指定任何ctor(后者已经有
a
虚拟继承,因此调用了默认值)

一个解决方案是删除虚拟继承…正如Arne Mertz的回答中提到的。 另一个(如果您确实想要虚拟继承)是从
C
ctor显式调用
A

C(int i, int j, int k} : A(i), B(i,j), _k(k){}

当您使用虚拟继承时,最派生的类必须直接调用其所有虚拟基的构造函数。在这种情况下,
C
的构造函数必须调用
B
A
的构造函数。因为您只调用
B
构造函数,所以它使用默认的
A
构造函数。它不使用
B
构造函数调用另一个
A
构造函数很重要:因为它是一个虚拟基类,所以忽略此调用

解决此问题有两种方法:显式调用
A(int)
构造函数:

C(int i, int j, int k} : A (i), B(i,j), _k(k){}

或者使用普通继承而不是虚拟继承。

为什么要声明虚拟继承?如果从类B中删除virtual关键字:virtual public A{…则代码可以正常工作。通过声明virtual A,C将直接调用A()。如果删除virtual,C将不调用A().

如果
A
是一个接口,而
B
扩展了该接口,那么虚拟继承实际上是唯一正确的解决方案。(当然,如果
A
是一个接口,它将没有任何数据成员,因此没有用户定义的构造函数。通常,虚拟继承只适用于接口。)@JamesKanze,这是错误的,如果你不从有共同祖先的2+类进行多重继承,你就不需要虚拟继承。此外,你可以在没有虚拟继承的情况下实现多个接口:例如,参见COM。我想我不明白你对“接口”的定义。你是指Java类型的接口吗(即没有成员的抽象类)但是,我见过C++中很多不同的东西可以称为ItNe饰面,但是它们都不需要虚拟继承。当然,在某些情况下抽象类可能会被纠缠在多个继承图中,但是既然它们没有成员,那么即使没有虚拟继承,VTABLE也不应该工作吗?在扩展接口时,您不知道是否会有其他扩展,也不知道某个具体类是否希望实现两个或多个扩展。因此,您必须虚拟继承(除非您处理的是一个小型、封闭的层次结构,您可以在必要时轻松返回并添加虚拟继承)或多或少地说,“阿诺梅兹”。它是C++中的一种常见模式,也是java语言中的一种常见模式(除了它在C++中工作之外,你的虚拟函数仍然是私有的,用非虚拟包装来检查前后条件)。如果你扩展了一个接口(与另一个接口),那么您通常应该虚拟继承.Thx以获得相应的答案。因此,我想我将回到Stroustrup来重新阅读虚拟继承的概念。默认情况下使用它似乎是不明智的;)许多人想知道为什么继承在默认情况下不是虚拟的。好吧,你自己已经找到了答案:)最派生的类不必显式地调用其虚拟基类的构造函数,它可以让它们像问题中那样隐式地默认初始化。但是它确实这样做了,不过,你是正确的,它总是最初始化虚拟基的派生类。@CharlesBailey重读我的答案,你是对的:我没有正确解释(这显然是误导)。我正在修复它。