多重继承时的执行顺序


class A: public B, public C { };

在这种情况下,执行顺序是:

B(); // base(first)  
C(); // base(second)  
A(); // derived

class A: public B, virtual public C { }; 

但是在这种情况下,当我在继承时用类c写virtual时,

的顺序
// execution becomes:  
C(); // virtual base  
B(); // ordinary base  
A(); // derived

我在某个地方读到,调用构造函数的顺序取决于继承多个类时声明的顺序,但是在用类写virtual时执行顺序如何改变。我不明白为什么会得到这样的结果。

根据c++标准,总是先执行虚基类构造函数。从N3242工作草案,272页第10行,我们得知:

  • 虚拟基类构造函数优先,按照从左到右深度优先遍历继承图的顺序。
  • 直接基类是下一个,按照声明顺序。

所以你看到的行为正是c++标准所要求的。这是有道理的,因为虚拟基类可能在继承中出现多次,当然每个基类只能构造一次。因此,必须先构造第一轮虚基类,然后再构造通常的非虚基类。

本页也有很好的解释

如果你有一个虚拟类作为父类,你不能希望初始化总是按照声明的顺序进行。事实上,第一个父类(比如非虚拟)本身可能依赖于虚拟类。因此,在这种情况下,必须首先构造虚类。

我想这就是为什么c++规范说虚拟父类的初始化函数总是首先执行的原因。如@Dan Roche所示,初始化顺序是可预测的。

的例子:

class B: public A {...}
class C: public B, virtual A {...}

在这个例子中,在C的初始化中,不可能在A之前初始化B,因为B的初始化需要先初始化A。

另一个例子

这个例子展示了你不应该依赖基类的初始化顺序:

#include <iostream>
using namespace std;
struct A {
  A() {cout<<"A()"<<endl;}
};
struct B {
  B() {cout<<"B()"<<endl;}
};
struct C: virtual A, virtual B {
  C() {cout<<"C()"<<endl;}
};
struct D: virtual B, virtual A, C {
  D() {cout<<"D()"<<endl;}
};  
int  main() {
  cout<<"construct C"<<endl;
  new C;
  cout<<"construct D"<<endl;
  new D;
}
输出:

construct C
A()
B()
C()
construct D
B()
A()
C()
D()

如例所示,当C被构造为D的基类时,a和B的初始化顺序颠倒了。这意味着如果你想让别人扩展你的类,你就不能依赖于虚拟基类初始化的顺序。

最新更新