继承中调用构造函数/析构函数的顺序

Bug*_*Bug 38 c++ constructor order-of-execution call-hierarchy

关于创建对象的一个​​小问题.说我有这两个类:

struct A{
    A(){cout << "A() C-tor" << endl;}
    ~A(){cout << "~A() D-tor" << endl;}
};

struct B : public A{
    B(){cout << "B() C-tor" << endl;}
    ~B(){cout << "~B() D-tor" << endl;}

    A a;
};
Run Code Online (Sandbox Code Playgroud)

并在主要我创建一个实例B:

int main(){
    B b;
}
Run Code Online (Sandbox Code Playgroud)

请注意,B派生自A并且还具有类型的字段A.

我想弄明白这些规则.我知道构造一个对象时首先调用它的父构造函数,反之亦然.

字段怎么样(A a;在这种情况下)?什么时候B创建,什么时候会调用它A的构造函数?我还没有定义初始化列表,是否有某种默认列表?如果没有默认列表?关于破坏的同样问题.

iam*_*ind 75

  • 施工总是从基地开始class.如果有多个基础,class那么构造从最左边的基础开始.(旁注:如果有virtual继承,则给予更高的偏好).
  • 然后构造成员字段.它们按声明的顺序初始化
  • 最后,它class本身就构建了
  • 析构函数的顺序正好相反

无论初始化列表如何,呼叫顺序都是这样的:

  1. Base class A的构造函数
  2. class B将构造名为a(类型class A)的字段
  3. 派生class B的构造函数

  • `最后,类本身是构造的` - 你在谈论构造函数体吗? (3认同)

650*_*502 22

假设没有虚拟/多重继承(这使事情变得复杂很多),那么规则很简单:

  1. 分配对象存储器
  2. 执行基类的构造函数,以大多数派生结束
  3. 执行成员初始化
  4. 该对象成为其类的真实实例
  5. 执行构造函数代码

需要记住的一件重要事情是,直到第4步,对象还不是其类的实例,因为它只有在构造函数的执行开始后才能获得此标题.这意味着如果在成员的构造函数期间抛出异常,则不会执行对象的析构函数,但只会破坏已构造的部分(例如成员或基类).这也意味着如果在成员或基类的构造函数中调用对象的任何虚拟成员函数,则调用的实现将是基础实现,而不是派生实现.另一个要记住的重要事项是,初始化列表中列出的成员将按照它们在类中声明的顺序构建,而不是按照它们在初始化列表中出现的顺序构建(幸运的是,如果列出成员,大多数正常的编译器都会发出警告以与班级宣言不同的顺序).

还要注意,即使在执行构造函数代码期间,this对象已经获得了它的最终类(例如,关于虚拟分派),除非构造函数完成其执行,否则不会调用类的析构函数.只有当构造函数完成执行时,对象实例才是实例中真正的第一类公民...在此之前只是一个"想成为实例"(尽管有正确的类).

破坏以完全相反的顺序发生:首先执行对象析构函数,然后它丢失其类(即从对象上的这一点被视为基础对象)然后所有成员以反向声明顺序销毁,最后基类销毁过程被执行到最抽象的父级.对于构造函数,如果在基类或成员析构函数中调用对象的任何虚拟成员函数(直接或间接),则执行的实现将是父实例,因为在类析构函数完成时对象丢失了其类标题.


Vau*_*ato 7

基类始终在数据成员之前构建.数据成员按照在类中声明的顺序构造.此顺序与初始化列表无关.初始化数据成员时,它将查看参数的初始化列表,如果没有匹配则调用默认构造函数.始终以相反的顺序调用数据成员的析构函数.