В каких обстоятельствах деструкторы С++ не будут называться?

Я знаю, что мои деструкторы вызываются при нормальном раскручивании стека и когда выбрасываются исключения, но не при вызове exit().

Есть ли другие случаи, когда мои деструкторы не будут вызваны? Как насчет сигналов, таких как SIGINT или SIGSEGV? Я полагаю, что для SIGSEGV они не вызываются, но для SIGNINT они есть, как я узнаю, какие сигналы развожу стек?

Есть ли другие обстоятельства, при которых они не будут вызваны?

Ответ 1

Есть ли другие обстоятельства, при которых они [деструкторы] не будут вызваны?

  • Длинные переходы: они мешают процессу разворачивания естественного стека и часто приводят к поведению undefined в С++.
  • Преждевременные выходы (вы уже указали на них, хотя стоит отметить, что бросание в то время, когда выполняется сброс стека в результате выброса исключения, приводит к поведению undefined, и поэтому мы никогда не должны выбрасывать из dtors)
  • Бросок из конструктора не вызывает dtor для класса. Вот почему, если вы выделяете несколько блоков памяти, управляемых несколькими разными указателями (а не интеллектуальными указателями) в ctor, вам нужно использовать блоки try на уровне функций или избегать использования списка инициализаторов и иметь блок try/catch в ctor (или, еще лучше, просто используйте умный указатель, например scoped_ptr, поскольку любой член, успешно инициализированный до сих пор в списке инициализаторов, будет уничтожен, даже если класс dtor не будет вызываться).
  • Как указано, неспособность сделать dtor virtual, когда класс удаляется с помощью базового указателя, может не вызывать поведение подкласса dtors (undefined).
  • Невозможность вызвать оператор сопоставления delete/delete [] для оператора new/new [] call (undefined поведение - может не вызвать dtor).
  • Невозможно вручную вызвать dtor при использовании нового места размещения с помощью специализированного распределителя памяти в разделе deallocate.
  • Использование таких функций, как memcpy, который копирует только один блок памяти в другой, не вызывая копирование ctors. mem * функции в С++ смертельны, поскольку они накапливают личные данные класса, перезаписывают vtables и т.д. Результатом обычно является поведение undefined.
  • Активация некоторых интеллектуальных указателей (auto_ptr) на неполном типе, см. этот обсуждение

Ответ 2

В стандарте С++ ничего не говорится о том, как обрабатывать конкретные сигналы - многие реализации могут не поддерживать SIGINT и т.д. Деструкторы не будут вызываться, если вызываются exit() или abort() или terminate().

Изменить: У меня только что был быстрый поиск по стандарту С++, и я не могу найти ничего, что указывало бы, как сигналы взаимодействуют с объектами жизни - возможно, кто-то с лучшими стандартами-фу, чем я мог найти что-то?

Дальнейшее редактирование:. Отвечая на другой вопрос, я нашел это в стандарте:

При выходе из области действия (однако), деструкторы (12.4) для всех построенных объектов с автоматической продолжительностью хранения (3.7.2) (названные объекты или временные) которые объявлены в этой области, в обратный порядок их декларация.

Итак, кажется, что деструкторы должны быть вызваны при получении сигнала.

Ответ 3

Сигнал сам по себе не будет влиять на выполнение текущего потока и, следовательно, на вызов деструкторов, потому что это другой контекст исполнения со своим собственным стеком, где ваши объекты не существуют. Это как прерывание: оно обрабатывается где-то вне контекста выполнения, и, если обрабатывается, элемент управления возвращается в вашу программу.

Как и при многопоточности, язык С++ не знает понятия сигналов. Эти два полностью ортогональны друг другу и определяются двумя несвязанными стандартами. Как они взаимодействуют, зависит от реализации, если она не нарушает ни один из стандартов.

В качестве побочного примечания другой случай заключается в том, что объект-деструктор не будет вызываться, когда его конструктор генерирует исключение. Однако деструкторы участников будут называться.

Ответ 4

В другом случае они не будут вызваны, если вы используете полиморфизм и не сделали ваши базовые деструкторы виртуальными.

Ответ 5

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

Ответ 6

Если функция или метод имеет спецификацию бросков и выбрасывает что-то, не охватываемое спецификацией, поведение по умолчанию заключается в немедленном выходе из системы. Стек не разматывается, и деструкторы не вызываются.

Сигналы POSIX представляют собой специфическую для операционной системы конструкцию и не имеют понятия области объектов С++. Как правило, вы не можете ничего делать с сигналом, за исключением, может быть, ловушки, установки глобальной переменной флага, а затем обрабатывать ее позже в коде С++ после выхода обработчика сигнала.

Последние версии GCC позволяют вам исключать из синхронных обработчиков сигналов, что приводит к ожидаемому процессу размотки и уничтожения. Это очень специфичная операционная система и компилятор, хотя

Ответ 7

Много ответов здесь, но все еще неполное!

Я нашел другой случай, когда деструкторы не выполняются. Это происходит всегда, когда исключение попадает на границу библиотеки.

Подробнее см. здесь:

Деструкторы не выполняются (без разворачивания стека) при выпуске исключения

Ответ 8

В основном есть две ситуации, где вызываются деструкторы: В стеке разматываются в конце функции (или на исключениях), если кто-то (или счетчик ссылок) вызывает delete.

В статических объектах обнаруживается одна особая ситуация - они разрушаются в конце программы через at_exit, но это все еще вторая ситуация.

Какой сигнал покидает at_exit, может зависеть, kill -9 немедленно уничтожит этот процесс, другие сигналы скажут ему о выходе, но как именно зависит от обратного вызова сигнала.