Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
1 / 1 / 0
Регистрация: 05.12.2024
Сообщений: 60

О корректном завершении программ на с++

01.11.2025, 19:49. Показов 6564. Ответов 77
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Доброго времени суток,

Изучая книгу Страуструппа "Язык программирования С++. Специальное издание", обратил внимание на следующее утверждение "При вызове exit() не будут вызваны деструкторы локальных переменных во всех функциях вверх по цепочке имевших место функциональных вызовов. Генерация и перехват исключений гарантируют корректное уничтожение локальных объектов. Вызов же exit() не позволяет корректно отработать всем функциям из цепочки вызовов. Поэтому лучше не ломать контекст вызова функций и просто сгенерировать исключение, а вопрос о том, что делать дальше, оставить обработчикам"

Допустим, в моей программе в месте, далеком по цепочке вызовов от main(), возникло событие, означающее что программа штатно закончило свою работу. Означает ли написанное выше что:
1) не стоит использовать библиотечные фукнции std::exit(), std::abort()
2) можно не заморачиваться с "правильным" завершением всех функций из цепочки вызовов, а достаточно сгенерировать исключение throw finish(), например. Разместить обработчик в main() и предусмотреть в нем вызов деструкторов глобальных объектов?

Хотелось бы понять и запомнить как правильно поступать при необходимости завершения сложных программ, от которых зависят в т.ч. другие процессы. Понятно, что простые учебные программы можно завершать как угодно.
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
01.11.2025, 19:49
Ответы с готовыми решениями:

Корректное закрытие программы при завершении работы Windows
Моя программа при старте сворачивается в трей и периодически выполняет определенные операции. Окно...

Корректное завершение работы сокетов
У меня система работает на синхронных сокетах. Для приема данных создается отдельный поток...

Корректное завершение потока
В программе по нажатию на кнопку запускается поток. В случае если закрыть окно, из которого запущен...

77
фрилансер
 Аватар для Алексей1153
6455 / 5656 / 1129
Регистрация: 11.10.2019
Сообщений: 15,062
01.11.2025, 20:15
Цитата Сообщение от ADnD Посмотреть сообщение
не стоит использовать библиотечные фукнции std::exit(), std::abort()
да, не нужно. Если честно - ни разу не встречал ситуацию, когда это могло понадобиться

Цитата Сообщение от ADnD Посмотреть сообщение
2) можно не заморачиваться с "правильным" завершением всех функций из цепочки вызовов, а достаточно сгенерировать исключение
кто-то любит исключения. А я предпочитаю без них. Не люблю

Цитата Сообщение от ADnD Посмотреть сообщение
как правильно поступать при необходимости завершения сложных программ, от которых зависят в т.ч. другие процессы
другие процессы вряд ли могут пострадать, если свой процесс завершился
2
425 / 147 / 27
Регистрация: 12.12.2020
Сообщений: 1,192
01.11.2025, 20:19
Можно не заморачиваться с вызовом деструкторов если речь идет только об освобождении выделенной памяти.
Другое дело если в деструкторах прописана какая то функциональная составляющая, например сохранить текущее состояние на диске, сохранить данные в базе данных, закрыть соединения и т.д.
Операционка очистит всю память, разорвет все соединения, освободит все занятые файлы. Вопрос чисто в работе программы.
0
фрилансер
 Аватар для Алексей1153
6455 / 5656 / 1129
Регистрация: 11.10.2019
Сообщений: 15,062
01.11.2025, 20:21
Цитата Сообщение от Alex1126 Посмотреть сообщение
Можно не заморачиваться с вызовом деструкторов
если наплевать на UB - то можно и не заморачиваться
0
425 / 147 / 27
Регистрация: 12.12.2020
Сообщений: 1,192
01.11.2025, 20:23
Цитата Сообщение от Алексей1153 Посмотреть сообщение
можно
а че вы так криво мою фразу процитировали? У меня там продолжение было которое несло смысл
0
 Аватар для eva2326
1673 / 501 / 107
Регистрация: 17.05.2015
Сообщений: 1,518
01.11.2025, 20:26
Цитата Сообщение от ADnD Посмотреть сообщение
Допустим, в моей программе в месте, далеком по цепочке вызовов от main(), возникло событие, означающее что программа штатно закончило свою работу.
В такой формулировке - это бред.
Если программа закончила свою работу, то программа уже не работает, она уже завершилась.
Событие "программа закончила свою работу" означает, что программы больше нет.
Завершившаяся программа принципиально не может реагировать на событие собственного завершения.

А кроме того, если факт завершения программы возник в месте, далеком по цепочке вызовов от main, тогда такое завершение - это аварийное завершение, а вовсе не штатное.

Штатное завершение - это, по-другому, "корректное завершение".
Для корректного завершения необходимо корректное завершение функции main.
Для этого нужно сначала дождаться корректного завершения всех дочерних потоков, и потом корректно выйти из функции main.

Обычно делают так: пускают сигнал о том, что нужно остановить всю работу. Поток функции main ждёт, когда все дочерние потоки завершатся, и потом тоже завершается. Кидать исключение для этого не нужно.
Механизм исключений предназначен для того, что бы обрабатывать нештатные ситуации, а не для того, что бы обрабатывать штатные.

Добавлено через 22 секунды
Цитата Сообщение от Алексей1153 Посмотреть сообщение
да, не нужно. Если честно - ни разу не встречал ситуацию, когда это могло понадобиться
Например: для кастомных ассертов.
2
фрилансер
 Аватар для Алексей1153
6455 / 5656 / 1129
Регистрация: 11.10.2019
Сообщений: 15,062
01.11.2025, 20:37
Alex1126, дальше не было смысла цитировать
0
Эксперт функциональных языков программированияЭксперт С++
 Аватар для Royal_X
6195 / 2896 / 1043
Регистрация: 01.06.2021
Сообщений: 10,635
01.11.2025, 20:56
Цитата Сообщение от Алексей1153 Посмотреть сообщение
Если честно - ни разу не встречал ситуацию, когда это могло понадобиться
при любом return из главной функции, либо просто при достижении конца главной функции, осуществляется нормальное завершение программы (с вызовом деструкторов переменных с авто сроком жизни) и выполняется std::exit() с тем же аргументом, что было у return, либо с 0, если явно такого аргумента не было.

Так что, все мы постоянно используем этот exit.
0
фрилансер
 Аватар для Алексей1153
6455 / 5656 / 1129
Регистрация: 11.10.2019
Сообщений: 15,062
01.11.2025, 21:21
Royal_X, ну, также можно сказать, что мы постоянно используем goto, ведь он там в машинном коде повсеместно

Цитата Сообщение от Royal_X Посмотреть сообщение
выполняется std::exit()
он таки выполняется после вызова всех деструкторов
0
Эксперт функциональных языков программированияЭксперт С++
 Аватар для Royal_X
6195 / 2896 / 1043
Регистрация: 01.06.2021
Сообщений: 10,635
01.11.2025, 21:24
Алексей1153, я всегда и говорю, что использую goto)
0
фрилансер
 Аватар для Алексей1153
6455 / 5656 / 1129
Регистрация: 11.10.2019
Сообщений: 15,062
01.11.2025, 21:32
Royal_X,
0
Эксперт функциональных языков программированияЭксперт С++
 Аватар для Royal_X
6195 / 2896 / 1043
Регистрация: 01.06.2021
Сообщений: 10,635
01.11.2025, 21:36
Алексей1153, чем тебе goto не нравится? Единственный нормальный инструмент для выхода из нескольких вложенных циклов. И это одобряется экспертами и даже Misra C++. Конечно, даже в этом случае вместо goto можно использовать функцию или прочие подходы, но все они отстой.
1
2642 / 1653 / 267
Регистрация: 19.02.2010
Сообщений: 4,377
01.11.2025, 22:13
Цитата Сообщение от ADnD Посмотреть сообщение
При вызове exit() не будут вызваны деструкторы локальных переменных во всех функциях вверх по цепочке имевших место функциональных вызовов.
Это так, но важнее другое.
У тебя С++, т.е. ООП с полиморфизмом. А также с условной компиляцией, возможной со времён С. И с возможностью передавать проге аргументы через командную строку.
Это означает, что и локальные, и даже глобальные объекты могут создаваться разными для разных билдов (для отладочного и для релизного), и/или при разных аргументах командной строки.
Например, в отладочном билде (или при некотором ключе, в зависимости от значения которого создаётся тот или иной наследник некоторого базового типа) объекты могут что-то логировать.
И "убивание" проги - оставит лог кривым (файл-то лога ОСь закроет - но вот его содержание останется "оборванным", не отразившим возможный возврат вверх по дереву вызовов).
Поэтому лучше штатно возвращаться вверх по цепочке вызовов. Ибо в сложных прогах (при коллективе разработчиков) - не ты можешь создавать/кодить какие-то лок.объекты / локальные вызовы, и не надо своим кодом рубить неизвестные тебе (или будущие потенциальные) возможности чужих объектов/частей проги.

Цитата Сообщение от ADnD Посмотреть сообщение
можно не заморачиваться с "правильным" завершением всех функций из цепочки вызовов, а достаточно сгенерировать исключение throw finish(), например. Разместить обработчик в main() и предусмотреть в нем вызов деструкторов глобальных объектов?
Тоже не вариант, по вышеописанным причинам.

Цитата Сообщение от Alex1126 Посмотреть сообщение
Другое дело если в деструкторах прописана какая то функциональная составляющая, например сохранить текущее состояние на диске, сохранить данные в базе данных, закрыть соединения и т.д.
Но ты можешь не знать, что за функциональность прописана в коде модуля, разрабатываемого другим человеком. Ты видишь интерфейс модуля (класса/классов в нём) - а времени изучать-проверять реализацию в этом чужом модуле у тебя может не быть.
Ну или ты можешь видеть только интерфейс базового (абстрактного класса), а каких там потомков с той или иной функциональностью породят затем от него другие кодеры - тебе неизвестно.
Т.е. лучше свою часть кода реализовывать так, чтобы не предполагать ничего о работе чужого кода.
1
1 / 1 / 0
Регистрация: 05.12.2024
Сообщений: 60
01.11.2025, 23:26  [ТС]
Цитата Сообщение от eva2326 Посмотреть сообщение
В такой формулировке - это бред.
Если программа закончила свою работу, то программа уже не работает, она уже завершилась.
Подразумевал, что получен искомый результат и программу надо завершать. Не ясно сформулировал свою мысль
Например, main() вызвало функцию модуля пользовательского интерфейса, оттуда был вызван управляющий модуль, который вызвал лексический анализатор, который получил данные от модуля работы с файлами и где-то, во глубине нескольких вложенных циклов нашел искомый результат

Цитата Сообщение от Royal_X Посмотреть сообщение
Алексей1153, чем тебе goto не нравится? Единственный нормальный инструмент для выхода из нескольких вложенных циклов. И это одобряется экспертами и даже Misra C++. Конечно, даже в этом случае вместо goto можно использовать функцию или прочие подходы, но все они отстой.
Во многом мой вопрос связан именно С ЭТИМ. Но я постеснялся об этом спросить прямо, чтобы меня на месте не расстреляли, не закидали тухлыми помидорами, яйцами (нужное подчеркнуть)
Я пользуюсь goto внутри функций, когда искомый результат получен, и надо перейти куда-то ближе к заветному return "что-то там". А тут, прочитав Страуструппа, что можно завершить программу выбросив исключение, в котором можно и структуру с данными пробросить, я подумал, а может быть это оно... И goto не нужно...

Прочитав Ваши ответы, я не увидел ни одного ответа ЗА завершение программы исключением, но увидел несколько ПРОТИВ. Не буду мусорить себе мозг этим "концептом".
0
Эксперт функциональных языков программированияЭксперт С++
 Аватар для Royal_X
6195 / 2896 / 1043
Регистрация: 01.06.2021
Сообщений: 10,635
01.11.2025, 23:40
ADnD, лучше выложи свой код или хотя бы часть.

Цитата Сообщение от ADnD
Я пользуюсь goto внутри функций, когда искомый результат получен, и надо перейти куда-то ближе к заветному return "что-то там".
Я как раз не из тех, кто с пеной из рта выступает за полный отказ от goto в языке С++. Считаю, что таким фанатиком не нужно быть. Но я что-то сомневаюсь, что именно в твоём случае goto нужен. Поэтому и советую тебе выложить код, чтобы обсуждать предметно.
0
Неэпический
 Аватар для Croessmah
18146 / 10730 / 2066
Регистрация: 27.09.2012
Сообщений: 27,030
Записей в блоге: 1
01.11.2025, 23:46
Лучший ответ Сообщение было отмечено Folian как решение

Решение

Цитата Сообщение от ADnD Посмотреть сообщение
во глубине нескольких вложенных циклов нашел искомый результат
И в чем проблема вернуться в main и нормально завершить её исполнение?
Цитата Сообщение от ADnD Посмотреть сообщение
а достаточно сгенерировать исключение throw finish()
Поворот не туда. Вы будете прокляты. Это будет печальное зрелище.
Исключения нужны для обработки ошибок, не нужно использовать их для костыльного управления выполнением.
Хотя, если вы злой колдун...
Цитата Сообщение от ADnD Посмотреть сообщение
не закидали тухлыми помидорами, яйцами (нужное подчеркнуть)
Вы пошли по тому же пути, но выбрали другие продукты.
Цитата Сообщение от ADnD Посмотреть сообщение
И goto не нужно...
А можно завернуть циклы в функцию-обертку и просто выйти из нее с нужным результатом.
2
фрилансер
 Аватар для Алексей1153
6455 / 5656 / 1129
Регистрация: 11.10.2019
Сообщений: 15,062
02.11.2025, 10:15
Цитата Сообщение от Royal_X Посмотреть сообщение
чем тебе goto не нравится
где написано, что он мне нравится или не нравится? Я к нему равнодушен.
Он мне ни разу не пригодился, ну и я даже не знаю, где бы он мог пригодиться

Добавлено через 44 секунды
Цитата Сообщение от Royal_X Посмотреть сообщение
все они отстой
почему же ? Это правильный способ
0
1 / 1 / 0
Регистрация: 05.12.2024
Сообщений: 60
02.11.2025, 10:22  [ТС]
Цитата Сообщение от Croessmah Посмотреть сообщение
И в чем проблема вернуться в main и нормально завершить её исполнение?
Так и делаю. Но прочитал текст, что возможен другой подход и призадумался - имеет ли он право на жизнь и практическое применение. Все полученные ответы говорят однозначно: "Нет".

Цитата Сообщение от Royal_X Посмотреть сообщение
ADnD, лучше выложи свой код или хотя бы часть.
Функция из тренировочной программы, которая ищет исходники программы на с++, анализирует на наличие директив #include, ищет заголовочные файлы, читает их и извлекает различную информацию, в данном случае о комментариях.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// анализирует текст заголовочного файла, определяет число строк с комментариями, без комментариев и количество слов в тексте
// @param   ptr_hS          указатель на структуру lexer::headerStruct для записи результатов
// @param headerText    указатель на массив c текстом заголовочного файла
void lexical_analizer::getInfoFromHeader(HeaderInfo* headerInfo, char* headerFile)
{
    char* txt = headerFile;
    size_t rows = 0;
    headerInfo->commented = 0;
    headerInfo->words = 0;
    while (*txt)
    {
        if (*txt == '/')
        {
            if (!*++txt) goto ENDWHILE;
            if (*txt == '/')
            {
                headerInfo->commented++;
                while (*txt != '\n') if (!*++txt) goto ENDWHILE;
                goto NEXT;
            }
            else if (*txt == '*')
                for (;;)
                {
                    headerInfo->commented++;
                    while (*txt != '\n')
                    {
                        if (!*++txt) goto ENDWHILE;
                        if (*txt == '*')
                        {
                            if (!*++txt) goto ENDWHILE;
                            if (*txt != '/') continue;
                            while (*txt != '\n') if (!*++txt) goto ENDWHILE;
                            goto NEXT;
                        }
                    }
                    if (!*++txt) goto ENDWHILE;
                }
        }
        if (*txt == '\n') rows++;
        NEXT:
        txt++;
    }
    ENDWHILE:
    headerInfo->notCommented = ++rows;
    txt = headerFile;
    while (*txt)
    {
        while (*txt == ' ' || *txt == '\t' || *txt == '\n' || *txt == '\r') if (!*++txt) return;
        while (*txt != ' ' && *txt != '\t' && *txt != '\n' && *txt != '\r') if (!*++txt) break;
        headerInfo->words++;
    }
}
Завершение программы производится "по классике", без throw finish(), и в данном конкретном случае throw не замена для goto, я это понимаю.
0
фрилансер
 Аватар для Алексей1153
6455 / 5656 / 1129
Регистрация: 11.10.2019
Сообщений: 15,062
02.11.2025, 11:04
ADnD, вот абсолютно то же самое

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
void lexical_analizer::getInfoFromHeader(HeaderInfo* headerInfo, char* headerFile)
{
    char* txt = headerFile;
    size_t rows = 0;
    headerInfo->commented = 0;
    headerInfo->words = 0;
    [&]//IIFE
    {
        while (*txt)
        {
            [&]
            {
                if (*txt == '/')
                {
                    if (!*++txt) return;
                    if (*txt == '/')
                    {
                        headerInfo->commented++;
                        while (*txt != '\n') if (!*++txt) return;
                        return;
                    }
                    else if (*txt == '*')
                        for (;;)
                        {
                            headerInfo->commented++;
                            while (*txt != '\n')
                            {
                                if (!*++txt) return;
                                if (*txt == '*')
                                {
                                    if (!*++txt) return;
                                    if (*txt != '/') continue;
                                    while (*txt != '\n') if (!*++txt) return;
                                    return;
                                }
                            }
                            if (!*++txt) return;
                        }
                }
                if (*txt == '\n') rows++;
            }();
            txt++;
        }
    }();
    headerInfo->notCommented = ++rows;
    txt = headerFile;
    while (*txt)
    {
        while (*txt == ' ' || *txt == '\t' || *txt == '\n' || *txt == '\r') if (!*++txt) return;
        while (*txt != ' ' && *txt != '\t' && *txt != '\n' && *txt != '\r') if (!*++txt) break;
        headerInfo->words++;
    }
}
Добавлено через 7 минут
вообще, тут, мне кажется, можно сильно упростить. Больно много проверок.

Но оптимизатор теперь и сам справится
1
1 / 1 / 0
Регистрация: 05.12.2024
Сообщений: 60
02.11.2025, 11:16  [ТС]
Цитата Сообщение от Алексей1153 Посмотреть сообщение
[&]//IIFE
Я еще не дошел до изучения такой конструкции как [&], пока осваиваю "базу". Что такое [&]?

Цитата Сообщение от Алексей1153 Посмотреть сообщение
вообще, тут, мне кажется, можно сильно упростить. Больно много проверок.
Проверки на случай, если встретится кривой косячный файл, завершаемый к примеру символом '/'. Решил перестраховаться. Сейчас работает стабильно. Может и можно упростить, но пока у меня опыта мало


P.S. Код исправил как вы посоветовали. Протестировал. Работает также
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
02.11.2025, 11:16
Помогаю со студенческими работами здесь

Корректное завершение работы консольного приложения
Есть консольное приложение на Qt, которое запускает несколько потоков. Фактически в main я создаю...

Корректное освобождение памяти при принудительном завершении потока TThread
У меня в отдельном потоке выполняются некоторые вычисления. В процессе выполнения этой функции...

Потоки и их корректное завершение
Доброго времени суток! Необходима Ваша помочь. Есть поток, организованный так: void...

Корректное завершение
Когда закрываю окно QGraphicsView`а, процесс остается еще запущен и пишет еще что возникла ошибка....

Корректное завершение QCoreApplication
Добрый день! OC: Windows 7 Professional Qt: 5.7.0 Есть приложение QCoreApplication: int...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
Символьное дифференцирование
igorrr37 13.02.2026
/ * Логарифм записывается как: (x-2)log(x^2+2) - означает логарифм (x^2+2) по основанию (x-2). Унарный минус обозначается как ! */ #include <iostream> #include <stack> #include <cctype>. . .
Камера Toupcam IUA500KMA
Eddy_Em 12.02.2026
Т. к. у всяких "хикроботов" слишком уж мелкий пиксель, для подсмотра в ESPriF они вообще плохо годятся: уже 14 величину можно рассмотреть еле-еле лишь на экспозициях под 3 секунды (а то и больше),. . .
И ясному Солнцу
zbw 12.02.2026
И ясному Солнцу, и светлой Луне. В мире покоя нет и люди не могут жить в тишине. А жить им немного лет.
«Знание-Сила»
zbw 12.02.2026
«Знание-Сила» «Время-Деньги» «Деньги -Пуля»
SDL3 для Web (WebAssembly): Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 12.02.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами и вызывать обработчики событий столкновения. . . .
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 11.02.2026
Содержание блога Библиотека SDL3 содержит встроенные инструменты для базовой работы с изображениями - без использования библиотеки SDL3_image. Пошагово создадим проект для загрузки изображения. . .
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL3_image
8Observer8 10.02.2026
Содержание блога Библиотека SDL3_image содержит инструменты для расширенной работы с изображениями. Пошагово создадим проект для загрузки изображения формата PNG с альфа-каналом (с прозрачным. . .
Установка Qt-версии Lazarus IDE в Debian Trixie Xfce
volvo 10.02.2026
В общем, достали меня глюки IDE Лазаруса, собранной с использованием набора виджетов Gtk2 (конкретно: если набирать текст в редакторе и вызвать подсказку через Ctrl+Space, то после закрытия окошка. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru