Порядок вызова конструкторов/деструкторов при наследовании

небольшой вопрос о создании объектов. Скажем, у меня есть эти два класса:

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

и в основном я создаю экземпляр B:

int main(){
    B b;
}

отметим, что B происходит от A, а также имеет поле типа A.

Я пытаюсь выяснить правила. Я знаю, что при построении объекта сначала вызывается его родительский конструктор, и наоборот при разрушении.

как насчет полей (A a; in это дело)? Когда B создается, когда он будет вызывать 'ы? Я не определил список инициализации, есть ли какой-то список по умолчанию? А если нет списка по умолчанию? И тот же вопрос о разрушении.

6 ответов


  • строительство всегда начинается с базы class. Если есть несколько base classes тогда строительство начинается с самой левой базы. (Примечание: если есть virtual наследование, тогда ему дается более высокое предпочтение).
  • затем создаются поля-члены. Они инициализируются в порядок им объявлен
  • наконец, построен
  • порядок деструктора является именно обратный

независимо от списка инициализаторов, порядок вызова будет такой:

  1. базовый class A'конструктор ы
  2. С именем a (типа class A) будет построен
  3. производные class B'конструктор с

предполагая, что нет виртуального / множественного наследования (что немного усложняет ситуацию), тогда правила просты:

  1. выделяется память объекта
  2. выполняются конструкторы базовых классов, заканчивающиеся на большинство производных
  3. инициализация члена выполняется
  4. объект становится истинным экземпляром своего класса
  5. выполняется код конструктора

важно помнить, что до шага 4 Объект еще не является экземпляром своего класса, потому что он получает этот заголовок только после начала выполнения конструктора. Это означает, что если во время конструктора элемента возникает исключение, деструктор объекта не выполняется, а уничтожаются только уже построенные части (например, члены или базовые классы). Это также означает, что если в конструкторе члена или базового класса вы вызываете любую виртуальную функцию-член объекта реализация called будет базовым, а не производным. Другая важная вещь, чтобы помнить, что членов, перечисленных в инициализации список будет построен в порядке их объявления в классе, а не в порядке их появления в списке инициализации списка (к счастью, большинство приличных компиляторов выдаст предупреждение, если вы перечислите членов в порядке, отличном от класса).

Обратите также внимание, что даже если во время выполнения кода конструктора this уже объект получил свой окончательный класс (например, в отношении виртуальной отправки) деструктор класса не будет вызываться, если конструктор завершает выполнение. Только когда конструктор завершает выполнение, экземпляр объекта является реальным гражданином первого класса среди экземпляров... до этого момента это только "wanna-Be instance" (несмотря на наличие правильного класса).

разрушение происходит в точном обратном порядке: сначала выполняется деструктор объекта, затем он теряет свой класс (т. е. с этого момента объект считается базовым объектом), затем все члены уничтожаются в обратном порядке объявления и, наконец, процесс уничтожения базового класса выполняется до самого абстрактного родителя. Что касается конструктора, если вы вызываете любую виртуальную функцию-член объекта (прямо или косвенно) в деструкторе базы или члена, то выполняемая реализация будет родительской, потому что объект потерял свой заголовок класса, когда деструктор класса завершенный.


базовые классы всегда создаются перед членами данных. Члены данных создаются в том порядке, в котором они объявлены в классе. Этот порядок не имеет ничего общего со списком инициализации. Когда элемент данных инициализируется, он просматривает список инициализации параметров и вызывает конструктор по умолчанию, если совпадение отсутствует. Деструкторы для элементов данных всегда вызываются в обратном порядке.


конструктор базового класса всегда выполняется первым.поэтому, когда вы пишете заявление 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 В А.
  2. вызывается B. m_A2::~A (). m_i = 5
  3. вызывается B. m_A1::~A (). m_i = 3 4 "Б"::~а() называется., 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