Ответ
Технически — да, но это крайне опасная практика, которой следует избегать. Деструктор — это часть процесса уничтожения объекта, и его основная задача — освобождение ресурсов, а не создание новых сущностей.
Критические риски:
-
Исключения: Начиная с C++11, деструкторы по умолчанию неявно объявлены как
noexcept(true). Если в таком деструкторе будет выброшено исключение (например, при неудачном выделении памяти для нового объекта), программа немедленно завершится черезstd::terminate(). -
Утечки памяти: Создание динамического объекта (
new) в деструкторе и потеря указателя на него — прямая дорога к утечке. -
Нарушение инвариантов и неопределенное поведение: Создание объекта может иметь побочные эффекты (изменение глобального состояния, блокировка мьютекса), которые нарушают ожидания от фазы разрушения.
Пример проблемного кода:
class Logger {
static std::vector<std::string>* globalLog;
public:
~Logger() noexcept(false) { // Опасно снимать noexcept!
// Попытка создать объект в деструкторе.
if (!globalLog) {
globalLog = new std::vector<std::string>(); // (1) Потенциальная утечка
}
globalLog->push_back("Logger destroyed"); // (2) Может выбросить std::bad_alloc
}
};
// Если push_back выбросит исключение, а деструктор noexcept(true) -> terminate.
Когда это может быть оправдано? Почти никогда. Единственный условно приемлемый случай — создание объекта с статической продолжительностью хранения (static), время жизни которого привязано ко всей программе, но и это часто признак плохого дизайна.
Правильная альтернатива: Логика, которая должна выполняться «после» разрушения объекта, должна быть вынесена в отдельный метод (например, shutdown(), finalize()), который вызывается явно до деструктора. Деструктор должен быть простым и надежным.
Ответ 18+ 🔞
Ну ты глянь, какая дичь в голову приходит людям. Создать объект в деструкторе? Да это ж, ёпта, как в последний день перед смертью решиться на третьего ребёнка зачать — логика на нуле, а последствия ебать какие непредсказуемые.
Технически-то компилятор проглотит, конечно. Но это тот самый случай, когда можно — не значит нужно. Деструктор — это финальная точка, место, где всё подметают и свет выключают, а не новый бизнес-проект запускают.
Слушай, вот главные причины, почему это пиздец как плохая идея:
-
Исключения — убийцы. В современных плюсах деструкторы по умолчанию
noexcept. Попробуй только ткнись сnewили любым другим броским вызовом — если память кончится и кинетstd::bad_alloc, твоя программа тут же накроется медным тазом черезstd::terminate(). Красиво? Нихуя. -
Утечки — наше всё. Создал объект через
newв деструкторе, а указатель куда записал? В локальную переменную, которая сейчас умрёт? Поздравляю, ты только что подарил системе кусок памяти, который уже никто и никогда не вернёт. Распиздяйство чистой воды. -
Полный разрыв шаблона. Фаза разрушения — это святое. Все ожидают, что там тихо и спокойно всё чистится. А ты начинаешь там свои инициализации проводить, глобальное состояние менять. Это как в крематории, пока тело в печь задвигают, начать ремонт в холле делать. Все охуеют.
Вот смотри на этот убогий пример, который только в голове долбоёба мог родиться:
class Logger {
static std::vector<std::string>* globalLog;
public:
~Logger() noexcept(false) { // Опасно снимать noexcept!
// Попытка создать объект в деструкторе.
if (!globalLog) {
globalLog = new std::vector<std::string>(); // (1) Потенциальная утечка
}
globalLog->push_back("Logger destroyed"); // (2) Может выбросить std::bad_alloc
}
};
// Если push_back выбросит исключение, а деструктор noexcept(true) -> terminate.
Видишь эту мартышлюшку? Всё в нём криво. И статический указатель, который может быть неинициализирован, и new в деструкторе, и push_back, который запросто может всё похерить. Доверия к такому коду — ебать ноль.
А есть ли оправдание? Хуй с горы. Ну, ладно, может один случай на миллион: создать объект со статической продолжительностью жизни (ну тот самый static), который переживёт всё. Но даже это — обычно признак того, что архитектор проекта — пидарас шерстяной, который накосячил с дизайном.
Как делать по-человечески? Да элементарно, чувак. Вся логика «последнего действия» или «перерождения» должна быть в отдельном методе. Назови его finalize(), shutdown(), releaseToTheWild() — не важно. Вызови этот метод явно, до того как объект начнёт разрушаться. А деструктор пусть будет тупым и надёжным, как лом: только освободить то, что было занято, и заткнуться. И всем будет спокойно.