Каков порядок, в котором деструкторы и конструкторы вызываются в С++? Использование примеров некоторых базовых классов и производных классов
Каков порядок, в котором деструкторы и конструкторы вызываются в С++
Ответ 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
Ответ 2
Подробное описание этих событий, включая виртуальное и множественное наследование, доступно в C++ FAQ Lite. Раздел 25.14 и 25.15
https://isocpp.org/wiki/faq/multiple-inheritance#mi-vi-ctor-order
Ответ 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. По сути, это правило "сначала построенный, последний разрушенный".
Конструкторы, вызывающие порядок:
- базовые конструкторы вызываются в порядке появления после ":"
- конструкторы-члены производного класса вызываются в порядке появления и перед конструктором класса
Деструкторы вызываются в обратном порядке вызываемых конструкторов.
Пример:
#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
