Необъяснимое поведение блока __try-__finally
Столкнулся с интересной ситуацией. Всегда думал, что операторы в блоке __finally исполняется всегда (конечно, кроме документированных случаев вроде завершения текущего потока в вышестоящем блоке __try).
Случайно обнаружил конструкцию в Borland C++ Builder 6.0, в которой также НЕ происходит исполнения операторов блока __finally:
{
try
{
throw("oops, an exception has occurred!");
}
catch(...)
{
return;
}
}
__finally
{
ShowMessage("now __finally block executes");
}
Здесь оператор "return" завершает текущую функцию, НЕ передавая перед этим управление блоку __finally!
Однако следующий пример работает как и положенно:
{
try
{
throw("oops, an exception has occurred!");
}
catch(...)
{
}
return;
}
__finally
{
ShowMessage("now __finally block executes");
}
Поясните пожалуйста, если кто сталкивался с чем-то подобным, нормальна ли эта ситуация. Заранее спасибо.
Не думаю, что так происходит. В описании к оператору __finally указано, что никакой из оператов, включая return, goto, break и и.д., не может просто взять и выйти из тела __try, не передав перед этим управление в блок __finally (если конечно он присутствует). Ведь в этом и есть основная идея оператора __finally - всегда выполнять некий финальный код независимо от ситуации в __try. Это больше похоже на глюк... ((
А в C++ такого и нету. Это спецефическая и нестандартная фича борландовского компилятора. Хотя встречаеся и в некоторых других. И как правило везде не рекомендуется исспользовать одновременно такой механизм исключений и стандартный С++. Я подозреваю, что тут тоже они тупо не могут работать вместе.
Это SEH -- structured exception handling -- часть операционной системы.
Рихтер:
Другое дело, что реализуется оно каждым компилятором по-своему.
Рихтер:
...
NOTE:
Не путайте SEH с обработкой исключении в С++, которая представляет собой еще одну форму обработки исключений, построенную на применении ключе вых слов языка С++ catch и throw
...
компилятор реализует обработку исключений С++ на основе SEH операционной системы. Например, когда Вы создаете С++ блок try, компилятор генерирует SEH-блок __try. С++ блок catch становится SEH-фильтром исключений, а код блока catch — кодом SEH-блока __except. По сути, обрабатывая С++ оператор throw, компилятор генерирует вызов Windows функции RaiseException, и значение переменной, указанной в throw, передастся этой функции как дополнительный аргумент
Наверное, при конструкции
{
__try
{
...
}
__except(EXCEPTION_EXECUTE_HANDLER) //catch(...)
{
return;
}
}
__finally
{
//не выполнится
}
оператор return в блоке __except останавливает глобальную раскрутку, и поэтому блок __finally не выполняется...
З.Ы. Проверьте на других компиляторах, у кого есть возможность!
Интересно, там такая штука ведет себя так же?
Ну я не пользуюсь борландовским компилятором, потому не могу утверждать просто это их фича или реализация SEH. )
Думаете, шифруются шпиёны? ;)
Потому и интересно, как поведут себя другие компиляторы, особенно от Micro$oft
Из моих наблюдений следует, что такая неправильная обработка блока __try-__finally происходит, если оператор return стоИт не непосредственно внутри __try-__finally, а в одном из вложенных блоков try-catch или __try-__finally. Получается, что нельзя здесь использовать вложенные конструкции...
{
__try
{
try
{
throw(1);
}
catch(...)
{
return; // <--- вот где вредитель!
}
}
__finally
{
// nothing to do
}
}
void caller()
{
while(1)
{
foo();
}
}
Легко убедиться, что подобный циклический вызов "неправильной" функции foo() быстро приводит к утечке памяти (проверяется в Диспетчере задач). Однако синтаксически код верен и вполне законен. Утечка памяти происходит из-за того, что оператор "return" не дает выполниться функции _CatchClean(), которая ответственна за освобождение ресурсов после обработи исключения (см. ассемблерный код функции foo()). Поэтому, на мой взгляд, это явная недоработка компилятора.
Знатоки советуют, чтобы избежать этого, не делать вложенных конструкций, а разводить их по разным функциям.