简体   繁体   English

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

[英]Order of calling constructors/destructors in inheritance

A little question about creating objects. 有关创建对象的一个​​小问题。 Say I have these two classes: 说我有这两节课:

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

and in main I create an instance of B : 在主我创建一个B的实例:

int main(){
    B b;
}

Note that B derives from A and also has a field of type A . 请注意, B派生自A并且还具有类型A的字段。

I am trying to figure out the rules. 我试图弄清楚规则。 I know that when constructing an object first calls its parent constructor, and vice versa when destructing. 我知道在构造对象时,首先调用其父构造函数,反之亦然。

What about fields ( A a; in this case)? 字段( A a;在这种情况下)呢? When B is created, when will it call A 's constructor? 创建B ,何时会调用A的构造函数? I haven't defined an initialization list, is there some kind of a default list? 我尚未定义初始化列表,是否存在某种默认列表? And if there's no default list? 如果没有默认列表? And the same question about destructing. 关于销毁的同样问题。

  • Construction always starts with the base class . 建设始终与基地启动class If there are multiple base class es then, construction starts with the left most base. 如果有多个基本class则从最左侧的基class开始构建。 ( side note : If there is a virtual inheritance then it's given higher preference). 旁注 :如果存在virtual继承,则它具有更高的优先级)。
  • Then the member fields are constructed. 然后构造成员字段。 They are initialized in the order they are declared 它们按照声明的顺序初始化
  • Finally, the class itself is constructed 最后, class本身被构造
  • The order of the destructor is exactly the reverse 析构函数的顺序恰好相反

Irrespective of the initializer list, the call order will be like this: 无论初始化列表如何,调用顺序都将如下所示:

  1. Base class A 's constructor class A的构造函数
  2. class B 's field named a (of type class A ) will be constructed 将构造class B的名为a (类型class A的类型)的字段
  3. Derived class B 's constructor 派生class B的构造函数

Assuming there is not virtual/multiple inheritance (that complicates things quite a bit) then the rules are simple: 假设没有虚拟/多重继承(这使事情复杂化了很多),那么规则很简单:

  1. The object memory is allocated 对象存储器已分配
  2. The constructor of base classes are executed, ending with most derived 基类的构造函数被执行,以大多数派生结尾
  3. The member initialization is executed 成员初始化执行
  4. The object becomes a true instance of its class 该对象成为其类的真实实例
  5. Constructor code is executed 构造函数代码被执行

One important thing to remember is that until step 4 the object is not yet an instance of its class, becuse it gains this title only after the execution of the constructor begins. 要记住的重要一件事是,在第4步之前该对象还不是其类的实例,因为该对象只有在构造函数的执行开始后才获得此标题。 This means that if there is an exception thrown during the constructor of a member the destructor of the object is not executed, but only already constructed parts (eg members or base classes) will be destroyed. 这意味着,如果在成员的构造函数期间引发异常,则不会执行对象的析构函数,而只会破坏已经构造的部分(例如,成员或基类)。 This also means that if in the constructor of a member or of a base class you call any virtual member function of the object the implementation called will be the base one, not the derived one. 这也意味着,如果在成员或基类的构造函数中调用对象的任何虚拟成员函数,则调用的实现将是基函数,而不是派生函数。 Another important thing to remember is that member listed in the initialization list will be constructed in the order they are declared in the class, NOT in the order they appear in the initialization list (luckily enough most decent compilers will issue a warning if you list members in a different order from the class declaration). 要记住的另一件重要事情是,初始化列表中列出的成员将按照在类中声明的顺序构造,而不是按照它们在初始化列表中出现的顺序构造(幸运的是,如果列出成员,大多数体面的编译器都会发出警告(与类声明的顺序不同)。

Note also that even if during the execution of constructor code the this object already gained its final class (eg in respect to virtual dispatch) the destructor of the class is NOT going to be called unless the constructor completes its execution . 还要注意,即使在执行构造函数代码期间, this对象已经获得了其最终类(例如,关于虚拟调度),也不会调用该类的析构函数,除非构造函数完成其执行 Only when the constructor completes execution the object instance is a real first class citizen among instances... before that point is only a "wanna-be instance" (despite having the correct class). 只有当构造函数完成执行时,对象实例才是实例中真正的一等公民……在那之前,只有“想成为实例”(尽管具有正确的类)。

Destruction happens in the exact reverse order: first the object destructor is executed, then it loses its class (ie from this point on the object is considered a base object) then all members are destroyed in reverse declaration order and finally the base class destruction process is executed up to the most abstract parent. 销毁以完全相反的顺序发生:首先执行对象析构函数,然后丢失其类(即,从该点开始,该对象被视为基础对象),然后按相反的声明顺序销毁所有成员,最后销毁基类执行到最抽象的父级。 As for the constructor if you call any virtual member function of the object (either directly or indirectly) in a base or member destructor the implementation executed will be the parent one because the object lost its class title when the class destructor completed. 对于构造函数,如果您在基或成员析构函数中调用对象的任何虚拟成员函数(直接或间接),则执行的实现将是父函数,因为当类析构函数完成时,对象会丢失其类标题。

Base classes are always constructed before data members. 基类始终在数据成员之前构造。 Data members are constructed in the order that they are declared in the class. 数据成员按照在类中声明的顺序构造。 This order has nothing to do with the initialization list. 此顺序与初始化列表无关。 When a data member is being initialized, it will look through your initialization list for the parameters, and call the default constructor if there is no match. 初始化数据成员时,它将在初始化列表中查找参数,如果不匹配,则调用默认构造函数。 Destructors for data members are always called in the reverse order. 数据成员的析构函数始终以相反的顺序调用。

Base class constructor always executes first.so when you write a statement B b; 基类构造函数始终首先执行。因此,当您编写语句B b; the constructor of A is called first and then the B class constructor.therefore the output from the constructors will be in a sequence as follows: 首先调用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;
}

The answer in this case is 2531. How constructor are called here: 在这种情况下,答案是2531。在这里如何调用构造函数:

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

The same-way Destructor is called: 相同的析构函数称为:

  1. B::~B() is called. B ::〜B()被调用。 ie m_i = 2, which decrement m_i to 1 in A. 即m_i = 2,这会在A中将m_i减为1。
  2. B.m_A2::~A() is called. B.m_A2 ::〜A()被调用。 m_i = 5 m_i = 5
  3. B.m_A1::~A() is called. B.m_A1 ::〜A()被调用。 m_i = 3 4 B::~A() is called., m_i = 1 m_i = 3 4 B ::〜A()被调用。,m_i = 1

In this example, construction of m_A1 & m_A2 is irrelevant of order of initialization list order but their declaration order. 在此示例中,m_A1和m_A2的构造与初始化列表顺序的顺序无关,但与它们的声明顺序无关。

Output from the modified code is: 修改后的代码的输出为:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM