在inheritance中调用构造函数/析构函数的顺序

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

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; }; 

在主要我创build一个B的实例:

 int main(){ B b; } 

请注意, B来自A并且还具有Atypes的字段。

我试图找出规则。 我知道当构造一个对象时,首先调用它的父构造函数,反之亦然。

怎么样的领域(在这种情况下)? 当B被创build时,它何时调用A的构造函数? 我还没有定义一个初始化列表,是否有某种默认列表? 如果没有默认列表? 和关于破坏的同样的问题。

  • build设总是从基础开始。 如果有多个基class ,则从最左边开始。 ( 注意 :如果有一个virtualinheritance,那么它给予更高的优先级)。
  • 然后它转向会员领域。 它们按照它们声明的顺序进行初始化
  • 最后,这个class本身就被构造了
  • 析构函数的顺序完全相反

不pipe初始化列表如何,调用层次将如下所示:

  1. class A的构造函数
  2. 字段A的对象创build
  3. 派生class B的构造函数

假设没有虚拟/多重inheritance(使事情复杂化很多),那么规则很简单:

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

一个重要的事情要记住的是,直到第4步的对象还不是它的类的一个实例,因为只有在构造函数的执行开始之后它才会获得这个标题。 这意味着如果在成员的构造函数中抛出exception,则不会执行该对象的析构函数,但是只有已构造的部分(例如成员或基类)才会被销毁。 这也意味着,如果在成员或基类的构造函数中调用该对象的任何虚拟成员函数,则所调用的实现将是基础实现,而不是派生的实现。 另一个要记住的重要的事情是,在初始化列表中列出的成员将按照它们在类中声明的顺序构build,而不是按它们在初始化列表中显示的顺序构build(幸运的是,如果列出成员,最正派的编译器将发出警告按照与类声明不同的顺序)。

还要注意的是,即使在构造函数代码的执行过程中, this对象已经获得了最终的类(例如关于虚拟调度),除非构造函数完成其执行,否则不会调用该类的析构函数。 只有当构造函数完成执行时,对象实例才是实例中真正的第一级公民……在此之前,这只是一个“想要成为实例”(尽pipe拥有正确的类)。

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

基类在数据成员之前总是被构造的。 数据成员按照在类中声明的顺序构造。 这个命令与初始化列表无关。 当一个数据成员被初始化时,它将通过初始化列表查看参数,如果不匹配,则调用默认的构造函数。 数据成员的析构函数总是以相反的顺序调用。

基类构造函数总是执行first.so当你写一个语句B b; A的构造函数首先被调用,然后是B类的构造函数。因此,构造函数的输出将按以下顺序排列:

 A() C-tor A() C-tor B() C-tor 
 #include<iostream> class A { public: A(int n=2): m_i(n) { // std::cout<<"Base Constructed with m_i "<<m_i<<std::endl; } ~A() { // std::cout<<"Base Destructed with m_i"<<m_i<<std::endl; std::cout<<m_i; } protected: int m_i; }; class B: public A { public: B(int n ): m_a1(m_i + 1), m_a2(n) { //std::cout<<"Derived Constructed with m_i "<<m_i<<std::endl; } ~B() { // std::cout<<"Derived Destructed with m_i"<<m_i<<std::endl; std::cout<<m_i;//2 --m_i; } private: A m_a1;//3 A m_a2;//5 }; int main() { { B b(5);} std::cout <<std::endl; return 0; } 

在这种情况下的答案是2531.如何在这里调用构造函数:

  1. B :: A(int n = 2)构造函数被调用
  2. B :: B(5)构造函数被调用
  3. B.m_A1 :: A(3)被调用
  4. B.m_A2 :: A(5)被调用

调用同一个析构函数:

  1. B ::〜B()被调用。 即m_i = 2,它将m_i减1为A.
  2. B.m_A2 ::〜A()被调用。 m_i = 5
  3. B.m_A1 ::〜A()被调用。 m_i = 3 4 B ::〜A()被调用,m_i = 1

在这个例子中,m_A1&m_A2的构造与初始化列表顺序的顺序无关,但与它们的声明顺序无关。

修改后的代码的输出是:

 A() C-tor A() C-tor B() C-tor ~B() D-tor ~A() D-tor ~A() D-tor