Order of calling constructors/destructors in inher

2019-01-02 15:21发布

问题:

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:

int main(){
    B b;
}

Note that B derives from A and also has a field of type 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)? When B is created, when will it call A's constructor? 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.

回答1:

  • Construction always starts with the base class. If there are multiple base classes then, construction starts with the left most base. (side note: If there is a virtual inheritance then it's given higher preference).
  • Then the member fields are constructed. They are initialized in the order they are declared
  • Finally, the class itself is constructed
  • 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
  2. class B's field named a (of type class A) will be constructed
  3. Derived class B's constructor


回答2:

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. 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 (e.g. 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 (e.g. in respect to virtual dispatch) the destructor of the class is NOT going to be called unless the constructor completes its execution. 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 (i.e. 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.



回答3:

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.



回答4:

Base class constructor always executes first.so when you write a statement 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() C-tor
A() C-tor
B() C-tor


回答5:

#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:

  1. B::A(int n=2) constructor is called
  2. B::B(5) constructor is called
  3. B.m_A1::A(3) is called
  4. B.m_A2::A(5) is called

The same-way Destructor is called:

  1. B::~B() is called. i.e m_i = 2, which decrement m_i to 1 in A.
  2. B.m_A2::~A() is called. m_i = 5
  3. B.m_A1::~A() is called. m_i = 3 4 B::~A() is called., m_i = 1

In this example, construction of m_A1 & m_A2 is irrelevant of order of initialization list order but their declaration order.



回答6:

Output from the modified code is:

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


标签: