Можно ли в деструкторе объекта создавать (порождать) другой объект?

Ответ

Технически — да, но это крайне опасная практика, которой следует избегать. Деструктор — это часть процесса уничтожения объекта, и его основная задача — освобождение ресурсов, а не создание новых сущностей.

Критические риски:

  1. Исключения: Начиная с C++11, деструкторы по умолчанию неявно объявлены как noexcept(true). Если в таком деструкторе будет выброшено исключение (например, при неудачном выделении памяти для нового объекта), программа немедленно завершится через std::terminate().

  2. Утечки памяти: Создание динамического объекта (new) в деструкторе и потеря указателя на него — прямая дорога к утечке.

  3. Нарушение инвариантов и неопределенное поведение: Создание объекта может иметь побочные эффекты (изменение глобального состояния, блокировка мьютекса), которые нарушают ожидания от фазы разрушения.

Пример проблемного кода:

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+ 🔞

Ну ты глянь, какая дичь в голову приходит людям. Создать объект в деструкторе? Да это ж, ёпта, как в последний день перед смертью решиться на третьего ребёнка зачать — логика на нуле, а последствия ебать какие непредсказуемые.

Технически-то компилятор проглотит, конечно. Но это тот самый случай, когда можно — не значит нужно. Деструктор — это финальная точка, место, где всё подметают и свет выключают, а не новый бизнес-проект запускают.

Слушай, вот главные причины, почему это пиздец как плохая идея:

  1. Исключения — убийцы. В современных плюсах деструкторы по умолчанию noexcept. Попробуй только ткнись с new или любым другим броским вызовом — если память кончится и кинет std::bad_alloc, твоя программа тут же накроется медным тазом через std::terminate(). Красиво? Нихуя.

  2. Утечки — наше всё. Создал объект через new в деструкторе, а указатель куда записал? В локальную переменную, которая сейчас умрёт? Поздравляю, ты только что подарил системе кусок памяти, который уже никто и никогда не вернёт. Распиздяйство чистой воды.

  3. Полный разрыв шаблона. Фаза разрушения — это святое. Все ожидают, что там тихо и спокойно всё чистится. А ты начинаешь там свои инициализации проводить, глобальное состояние менять. Это как в крематории, пока тело в печь задвигают, начать ремонт в холле делать. Все охуеют.

Вот смотри на этот убогий пример, который только в голове долбоёба мог родиться:

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() — не важно. Вызови этот метод явно, до того как объект начнёт разрушаться. А деструктор пусть будет тупым и надёжным, как лом: только освободить то, что было занято, и заткнуться. И всем будет спокойно.