Ответ
В C++ деструкторы вызываются в строго определённом порядке, обратном порядку конструирования:
- Для одного объекта: Тело деструктора самого класса выполняется первым.
- Для членов класса (полей): Деструкторы полей вызываются после тела деструктора класса, в порядке, обратном их объявлению в определении класса.
- Для базовых классов: Деструкторы базовых классов вызываются после деструкторов всех полей, в порядке, обратном порядку наследования.
- Для объектов в стеке: При выходе из области видимости деструкторы локальных объектов вызываются в порядке LIFO (последний созданный — первый уничтоженный).
Пример, демонстрирующий порядок:
#include <iostream>
#include <string>
class Member1 {
public:
~Member1() { std::cout << "Member1 destroyed.n"; }
};
class Member2 {
public:
~Member2() { std::cout << "Member2 destroyed.n"; }
};
class Base {
public:
~Base() { std::cout << "Base destroyed.n"; }
};
class Derived : public Base {
Member1 m1; // Объявлен первым
Member2 m2; // Объявлен вторым
public:
~Derived() {
std::cout << "Derived body.n"; // Тело деструктора
}
};
int main() {
Derived d;
// При выходе из main() для объекта 'd' порядок будет:
// 1. Тело ~Derived()
// 2. ~Member2() (обратный порядок объявления полей)
// 3. ~Member1()
// 4. ~Base() (обратный порядок наследования)
return 0;
}
Ожидаемый вывод:
Derived body.
Member2 destroyed.
Member1 destroyed.
Base destroyed. Ответ 18+ 🔞
А, ну это классика, ёпта! Сижу тут, кофе попиваю, и вспомнил, как один мой знакомый чувак на ровном месте накрылся медным тазом из-за того, что порядок деструкторов не понимал. Ну, думаю, надо про это рассказать, а то народ потом охуевает, когда программа падает, а почему — хуй поймёшь.
В общем, в C++ эта штука работает чётко, как часы, но если не знать правил, то можно сам от себя охуеть. Деструкторы вызываются в строго определённом порядке, и это не просто так, а чтобы всё красиво разобрать, как конструктор собрал.
Вот смотри, как это работает, на пальцах:
- Для одного объекта: Сначала выполняется само тело деструктора класса. То есть твой код, который ты там написал в
~Derived(), отрабатывает самым первым. Это логично, ты же сначала свою внутреннюю кухню прибираешь, а потом уже помощников распускаешь. - Для членов класса (полей): А вот потом, уже после твоего кода, начинают вызываться деструкторы полей-объектов. И вызываются они в обратном порядке относительно того, как ты их объявил в классе. Кто последний объявлен — тот первый и уничтожается. Почему? А хуй его знает, так исторически сложилось, но это железное правило.
- Для базовых классов: И только когда все поля разобраны, наступает черёд базовых классов. Их деструкторы вызываются тоже в обратном порядке наследования. Если ты наследуешься от
Base, то~Base()вызовется в самом конце. - Для объектов в стеке: Ну и отдельная песня — локальные объекты в функции. Тут принцип простой, как мир: последний созданный — первый на выход. LIFO, как в стопке тарелок. Поставил сверху — сверху же и снял.
А теперь, чтобы не быть голословным, вот тебе живой пример, который всё расставит по полочкам. Смотри на этот код и не бзди.
#include <iostream>
#include <string>
class Member1 {
public:
~Member1() { std::cout << "Member1 destroyed.n"; }
};
class Member2 {
public:
~Member2() { std::cout << "Member2 destroyed.n"; }
};
class Base {
public:
~Base() { std::cout << "Base destroyed.n"; }
};
class Derived : public Base {
Member1 m1; // Объявлен первым
Member2 m2; // Объявлен вторым
public:
~Derived() {
std::cout << "Derived body.n"; // Тело деструктора
}
};
int main() {
Derived d;
// При выходе из main() для объекта 'd' порядок будет:
// 1. Тело ~Derived()
// 2. ~Member2() (обратный порядок объявления полей)
// 3. ~Member1()
// 4. ~Base() (обратный порядок наследования)
return 0;
}
И что же мы увидим, когда программа закончится и объект d начнёт разбираться? А вот что, ядрёна вошь:
Derived body.
Member2 destroyed.
Member1 destroyed.
Base destroyed.
Вот и вся магия. Сначала своё тело, потом поля задом наперёд, а в самом конце — батя. Запомни эту последовательность, и волнение ебать по поводу утечек памяти или кривого разрушения ресурсов у тебя отпадёт. Главное — не выёбывайся и не пытайся вручную в деструкторе члены удалять, которые сами удаляться должны. А то будет тебе хиросима, а не программа.