Подтвердить что ты не робот

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

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

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; в этом случае)? Когда B создается, когда он вызывает конструктор A? Я не определил список инициализации, есть ли какой-то список по умолчанию? А если нет списка по умолчанию? И тот же вопрос о деструкции.

4b9b3361

Ответ 1

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

Независимо от списка инициализаторов иерархия вызовов будет выглядеть следующим образом:

  • Конструктор базы class A
  • Создание поля A
  • Производный конструктор class B

Ответ 2

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

  • Память объекта выделена
  • Выполняется конструктор базовых классов, заканчивающийся на большинстве полученных
  • Выполняется инициализация члена
  • Объект становится истинным экземпляром своего класса
  • Выполняется выполнение кода конструктора

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

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

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

Ответ 3

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

Ответ 4

Конструктор базового класса всегда выполняет first.so, когда вы пишете оператор B b;, сначала вызывается конструктор A, а затем конструктор класса B. Поэтому вывод из конструкторов будет в последовательности следующим образом

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

Ответ в этом случае - 2531. Как здесь вызывается конструктор:

  • Конструктор
  • B:: A (int n = 2) называется Конструктор
  • B:: B (5) называется
  • B.m_A1:: A (3) называется
  • B.m_A2:: A (5) называется

Вызывается тот же самый способ деструктора:

  • B:: ~ B(). i.e m_i = 2, который уменьшает m_i до 1 в A. Вызывается
  • B.m_A2:: ~ A(). m_i = 5 Вызывается
  • B.m_A1:: ~ A(). m_i = 3 4 B:: ~ A() называется., M_i = 1

В этом примере построение m_A1 и m_A2 не имеет отношения к порядку порядка списка инициализации, но их порядок объявления.

Ответ 6

Выход из измененного кода:

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