Каков порядок, в котором деструкторы и конструкторы вызываются в С++? Использование примеров некоторых базовых классов и производных классов
Каков порядок, в котором деструкторы и конструкторы вызываются в С++
Ответ 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