Каков порядок, в котором деструкторы и конструкторы вызываются в С++

Каков порядок, в котором деструкторы и конструкторы вызываются в С++? Использование примеров некоторых базовых классов и производных классов

Ответ 1

Порядок:

  • Базовый конструктор
  • Производный конструктор
  • Производный деструктор
  • Базовый деструктор

Пример:

class B
{
public:
  B()
  {  
    cout<<"Construct B"<<endl;
  }

  virtual ~B()
  {
    cout<<"Destruct B"<<endl;
  }
};

class D : public B
{
public:
  D()
  {  
    cout<<"Construct D"<<endl;
  }

  virtual ~D()
  {
    cout<<"Destruct D"<<endl;
  }
};



int main(int argc, char **argv)
{
  D d; 
  return 0;
}

Результат примера:

Конструкция B

Построить D

Уничтожить D

Уничтожить B

Несколько уровней наследования работают как стек:

Если вы подумали о том, чтобы вытащить элемент в стек как конструкцию и снять его с уничтожения, вы можете посмотреть на несколько уровней наследования, например, на стек.

Это работает для любого количества уровней.

Пример D2 происходит от D происходит от B.

Нажмите B в стеке, нажмите D в стеке, нажмите D2 в стеке. Таким образом, порядок построения - B, D, D2. Затем, чтобы узнать, как начинается порядок уничтожения. D2, D, B

Более сложные примеры:

Для более сложных примеров см. ссылку, предоставленную @JaredPar

Ответ 3

Кроме того, имейте в виду, что, хотя элементы массива строятся сначала → последние, они разрушаются в обратном порядке: последний → первый.

Ответ 4

Я должен добавить к предыдущим ответам, потому что все, кажется, игнорируют его

Если у вас есть полученный экземпляр класса , созданный, это правда, что код внутри конструктор base будет называться до кода внутри конструктора полученного, но имейте в виду, что > по-прежнему технически "создан до базы.

И когда вы вызываете деструктор класса производный, верно, что код внутри производный деструктор называется до кода внутри базового деструктора, но также помните, что base уничтожен до /STRONG > .

Когда я говорю created/destroy, я на самом деле ссылаюсь на выделенные/освобожденные.

Если вы посмотрите на макет памяти этих экземпляров, вы увидите, что производный экземпляр содержит базовый экземпляр. Например:

Память производной: 0x00001110 до 0x00001120

Память базы: 0x00001114 до 0x00001118

Следовательно, производному классу должно быть выделено BEFORE база в конструкции. И производный класс должен быть освобожден ПОСЛЕ базы в уничтожении.

Если у вас есть следующий код:

class Base 
{
public:
    Base()
    {
        std::cout << "\n  Base created";
    }
    virtual ~Base()
    {
        std::cout << "\n  Base destroyed";
    }
}

class Derived : public Base 
{
public:
    Derived()
    // Derived is allocated here 
    // then Base constructor is called to allocate base and prepare it
    {
        std::cout << "\n  Derived created";
    }
    ~Derived()
    {
        std::cout << "\n  Derived destroyed";
    }   
    // Base destructor is called here
    // then Derived is deallocated
}

Итак, если вы создали Derived d; и вышли из области видимости, то вы получите результат в ответе @Brian. Но поведение объекта в памяти на самом деле не в том же порядке, это примерно так:

Строительство:

  • Производится выделение

  • Базовая выделенная

  • Базовый конструктор, называемый

  • Производный конструктор, называемый

Destruction:

  • Производный деструктор, называемый

  • Базовый деструктор, называемый

  • База освобождена

  • Выведено освобождение

Ответ 5

Это ясно описано в order-dtors-for-members. По сути, это правило "сначала построенный, последний разрушенный".

Конструкторы, вызывающие порядок:

  1. базовые конструкторы вызываются в порядке появления после ":"
  2. конструкторы-члены производного класса вызываются в порядке появления и перед конструктором класса

Деструкторы вызываются в обратном порядке вызываемых конструкторов.

Пример:

#include <iostream>

struct base0 {  base0(){printf("%s\n", __func__);};~base0(){printf("%s\n", __func__);}; };
struct base1 { base1(){printf("%s\n", __func__);}; ~base1(){printf("%s\n", __func__);};};
struct member0 { member0(){printf("%s\n", __func__);};  ~member0(){printf("%s\n", __func__);};};
struct member1 { member1(){printf("%s\n", __func__);}; ~member1(){printf("%s\n", __func__);};};
struct local0 { local0(){printf("%s\n", __func__);}; ~local0(){printf("%s\n", __func__);}; };
struct local1 { local1(){printf("%s\n", __func__);};  ~local1(){printf("%s\n", __func__);};};
struct derived: base0, base1
{
  member0 m0_;
  member1 m1_;
  derived()
  {
    printf("%s\n", __func__);
    local0 l0;
    local1 l1;
  }
  ~derived(){printf("%s\n", __func__);};
};
int main()
{
  derived d;
}

Выход:

base0
base1
member0
member1
derived
local0
local1
~local1
~local0
~derived
~member1
~member0
~base1
~base0