Я пытался прочитать разницу между return EXIT_SUCCESS;
от main()
и звонит exit(EXIT_SUCCESS)
из любого места, и лучший ресурс, который я нашел, этот ответ здесь на SO. Однако есть одна деталь, которую я хотел бы прояснить.
Для меня самый убедительный аргумент против exit()
(как изложено в этом посте), что деструктор не вызывается на объектах локальной области действия. Но что это значит для Другой объекты? Что делать, если я звоню exit()
откуда-то еще, довольно далеко от стека main()
метод, но в блоке (даже метод), который содержит только этот вызов, и без переменных? Будут ли уничтожены объекты в другом месте в стеке?
Мой вариант использования такой:
У меня есть приложение, которое продолжает запрашивать ввод у пользователя, пока не будет дана команда «выйти» (текстовая приключенческая игра). Самый простой способ сделать это — сопоставить метод quit с методом, который просто вызывает exit(EXIT_SUCCESS)
, Конечно, я мог бы написать это так, чтобы каждый действие, которое пользователь может предпринять, возвращает логическое значение, указывающее, должна ли игра продолжаться или нет, а затем просто return false
когда я хочу выйти — но только раз я бы вернул ничего, кроме true
от этого метода — каждый другой метод действия должен был бы тогда return true
только потому, что я хотел избежать exit()
, С другой стороны, я создаю довольно много объектов и динамически выделяю достаточно памяти — обо всем этом должны заботиться деструкторы классов, поэтому очень важно, чтобы они выполнялись.
Какова лучшая практика здесь? Это хороший случай для exit()
или так же плохо, как в main
метод?
if (command == "quit") {
throw QuitGameException();
}
Вы могли бы бросить исключение. Исключение будет безопасно разматывать стек и уничтожать объекты во всех вызывающих по пути.
Я даже не собираюсь читать этот пост, потому что я знаю, что он говорит. Не используйте выход (), так что не надо.
Я знаю одну причину использовать exit () — если вы в любом случае полностью обречены, и вы никак не можете выйти. В таком случае вы будете не выйти с кодом ноль. Итак, выход () с ненулевым значением, когда вы все равно собираетесь потерпеть крах.
В любой другой случай, создайте переменные, которые позволят вам покинуть основные циклы и выйти из main nice и sane, чтобы очистить всю вашу память. Если вы не напишите такой код, вы, например, никогда не сможет обнаружить все утечки памяти.
Будут ли уничтожены объекты в другом месте в стеке?
Нет, exit () делает следующее (по порядку):
- Объекты, связанные с текущим потоком с продолжительностью хранения потока, уничтожаются (только C ++ 11).
- Объекты со статической продолжительностью хранения уничтожаются (C ++) и вызываются функции, зарегистрированные с помощью atexit (если вызывается необработанное исключение, вызывается завершение).
- Все потоки C (открытые с функциями в) закрываются (и сбрасываются, если буферизируются), и все файлы, созданные с помощью tmpfile, удаляются.
- Управление возвращается в среду хоста
exit () не разматывает стек, память для всего стека просто освобождается, деструктор для отдельных объектов в стеке не запускается. Использование exit () безопасно только тогда, когда все объекты, у которых нет простых деструкторов (тех, которые не имеют дело с внешними ресурсами), размещены в статическом хранилище (то есть глобальные переменные или статическая переменная локальной области действия). Большинство программ имеют обработчики файлов, соединения с сокетами, обработчики баз данных и т. Д., Которые могут выиграть от более плавного завершения работы. Обратите внимание, что динамически распределяемый объект (который не связан с внешними ресурсами) не обязательно должен быть освобожден, поскольку программа все равно собирается завершиться.
exit () — это функция, унаследованная от C, которая не имеет деструктора, поэтому очистка внешних ресурсов всегда может быть организована с помощью atexit (); в общем, очень трудно безопасно использовать exit () в C ++, вместо этого в C ++ вы должны написать свою программу в RAII и вызвать исключение для завершения и очистки.