В определенных случаях - особенно, когда исключение выходит из деструктора во время раскручивания стека - вызовы времени выполнения C++ terminate()
который должен сделать что-то разумное вскрытие и затем выйти из программы. Когда вопрос, "почему так резкий" возникает ответ, обычно "нет ничего более разумного, чтобы сделать в таких ошибочных ситуациях". Это звучит разумным, если целая программа находится в C++.
Теперь, что, если код C++ находится в библиотеке и программе, которая пользуется библиотекой, не находится в C++? Это происходит довольно часто - например, у меня мог бы быть собственный COM-компонент C++, использованный программой.NET. Однажды terminate()
назван в коде компонента, который программа.NET внезапно заканчивает неправильно. Автор программы будет, в первую очередь, думать, что "Я не забочусь о C++, какого черта эта библиотека, заставляют мою программу выйти?"
Как я обрабатываю последний сценарий при разработке библиотек в C++? Действительно ли это разумно это terminate()
неожиданно заканчивает программу? Существует ли лучший способ обработать такие ситуации?
Почему среда выполнения C ++ вызывает terminate ()
? Он не делает это случайно или из-за обстоятельств, которые нельзя определить и / или избежать при написании кода. Это происходит потому, что ваш код делает что-то, что определено для вызова terminate ()
, например, генерирует исключение из деструктора во время раскрутки стека.
В стандарте C ++ есть список всех ситуаций, которые определены как результат вызова terminate ()
. Если вы не хотите, чтобы вызывалась terminate ()
, не делайте ничего из этого в своем коде. То же самое относится к неожиданным ()
, abort ()
и т. Д.
Я не думаю, что это действительно отличается от того факта, что вы должны избегать неопределенного поведения или вообще избегать написания неправильного кода. Вы также должны избегать поведения, которое определено, но нежелательно.
Возможно, у вас есть конкретный пример, в котором трудно избежать вызова terminate ()
, но выброс исключения из деструктора во время раскрутки стека не так. Только никогда не выбрасывайте исключения из деструкторов. Это означает разработку деструкторов таким образом, чтобы, если они делают что-то, что может привести к сбою, деструктор перехватывает исключение, и ваш код продолжает работу в определенном состоянии.
Бывают ситуации, когда ваша система невежливо взломает ваш процесс из-за чего-то, что делает ваш код C ++ (хотя и не путем вызова terminate ()
).Например, если система чрезмерно выделяет память и VMM не может выполнить обещания malloc / new
, то ваш процесс может быть остановлен. Но это системная функция и, вероятно, в равной степени применима к другому языку, вызывающему ваш C ++. Я не думаю, что вы можете (или должны) что-то с этим сделать, если вызывающий абонент знает, что ваша библиотека может выделить память. В этом случае процесс завершился не по вине вашего кода, а с определенной реакцией операционной системы на нехватку памяти.
Я думаю, что более фундаментальный вопрос заключается не в том, что делает terminate, а в дизайне библиотеки. Библиотека может быть разработана для использования только с C ++, и в этом случае исключения могут быть перехвачены и обработаны соответствующим образом в приложении.
Если библиотека предназначена для использования в сочетании с приложениями, отличными от C ++, ей требуется , чтобы предоставить интерфейс, который гарантирует, что исключения не покидают библиотеку, например интерфейс, который выполняет перехват (.. .).
Предположим, у вас есть функция на C ++ с именем cppfunc
и вы вызываете его из другого языка (например, C или .NET). Я предлагаю вам создать функцию-оболочку, скажем exportedfunc
, например так:
int exportedfunc(resultype* outresult, paramtype1 param1, /* ... */) { try { *outresult = cppfunc(param1,param2,/* ... */); return 0; // indicate success }catch( ... ) { // may want to have other handlers /* possibly set other error status info */ return -1; // indicate failure } }
По сути, вам нужно убедиться, что исключения не пересекают языковые границы ... поэтому вам нужно обернуть свои функции C ++ функция, которая перехватывает все исключения и сообщает код состояния или делает что-то приемлемое, кроме вызова std :: terminate.
Обработчик завершения по умолчанию вызовет abort
. Если вам не нужно такое поведение, определите свой собственный обработчик завершения и установите его с помощью set_terminate .