C ++, __try и try / catch / finally - PullRequest
       30

C ++, __try и try / catch / finally

42 голосов
/ 13 августа 2011

Мне немного интересно узнать о блоках C ++ try / catch / finally. Я видел эти команды с двумя подчеркиваниями, такими как __try. Но проекты MVSC 2010 также работают без подчеркивания. Итак, когда вам нужны эти подчеркивания?

Ответы [ 3 ]

88 голосов
/ 13 августа 2011

В Windows исключения поддерживаются на уровне операционной системы. Названные Структурной обработкой исключений (SEH), они являются грубым эквивалентом сигналов Unix. Компиляторы, которые генерируют код для Windows, обычно используют это преимущество, они используют инфраструктуру SEH для реализации исключений C ++.

В соответствии со стандартом C ++ ключевые слова throw и catch только генерируют и перехватывают исключения C ++. Соответствующий код исключения SEH для компилятора MSVC - 0xe06d7343. Последние 3 байта являются кодом ASCII для "msc".

Унификация с поддержкой операционной системы также означает, что деструкторы C ++ будут вызываться при разматывании стека для исключения SEH. Код, который выполняет раскручивание, находится внутри Windows и обрабатывает SEH, вызванный throw , точно так же, как любой SEH. Тем не менее, компилятор Microsoft имеет оптимизацию, которая пытается избежать генерации необходимого кода, который обеспечивает вызов деструкторов во всех случаях. Если он может доказать, что в блоке контекста, управляющего временем жизни объекта, нет оператора throw, он пропускает регистрационный код. Это несовместимо с асинхронными исключениями SEH, вы должны использовать опцию компиляции / EHa, чтобы подавить эту оптимизацию, если вы собираетесь перехватывать исключения SEH.

Существует много типов исключений SEH. Те, которые могут быть сгенерированы операционной системой, перечислены в заголовочном файле SDK ntstatus.h. Кроме того, вы можете взаимодействовать с кодом, который использует SEH для реализации собственной обработки исключений, они будут использовать свой собственный код исключения. Как и .NET, управляемые исключения используют код исключения 0xe0434f4d ("com").

Чтобы перехватить исключения SEH в программе на C ++, вы должны использовать нестандартное ключевое слово __try. Ключевое слово __except аналогично ключевому слову C ++ catch . У него больше возможностей, вы указываете выражение фильтра исключений, которое определяет, следует ли перехватывать активное исключение. Все возможно, но вы, как правило, просматриваете только переданную информацию об исключении, чтобы узнать, заинтересованы ли вы в ее обработке. Ключевое слово __finally позволяет писать код, который запускается после обработки исключения. Этого нет в C ++, но нередко в других языках.

Все это довольно плохо документировано, как указано в комментариях. Доказательство в пудинге. Вот пример программы, с которой вы можете играть. Он демонстрирует, как исключения SEH по-прежнему позволяют вызывать деструкторы C ++ при условии, что вы компилируете с / EHa, и как исключения C ++ реализованы поверх SEH. Требуется компилятор MSVC, используйте Ctrl + F5, чтобы избежать необходимости использования отладчика:

#include "stdafx.h"
#include <windows.h>
#include <iostream>

// NOTE: the value of the C/C++, Code Generation, Enable C++ Exceptions setting in important
// Try it both with /EHsc (the default) and /EHa to see the difference

class Example {  
public:
    ~Example() { std::cout << "destructed" << std::endl; }
};

int filterException(int code, PEXCEPTION_POINTERS ex) {
    std::cout << "Filtering " << std::hex << code << std::endl;
    return EXCEPTION_EXECUTE_HANDLER;
}

void testProcessorFault() {
    Example e;
    int* p = 0;
    *p = 42;
}

void testCppException() {
    Example e;
    throw 42;
}

int main()
{
    __try {
        testProcessorFault();
    }
    __except(filterException(GetExceptionCode(), GetExceptionInformation())) {
        std::cout << "caught" << std::endl;
    }
    __try {
        testCppException();
    }
    __except(filterException(GetExceptionCode(), GetExceptionInformation())) {
        std::cout << "caught" << std::endl;
    }
    return 0;
}

Выход:

Filtering c0000005
destructed
caught
Filtering e06d7363
destructed
caught
22 голосов
/ 13 августа 2011

__try / __except для перехвата SEH (ошибки, генерируемые окнами) не для перехвата общих исключений.

try / catch - это то, что стандарт C ++ определяет для обработки общих исключений C ++.

Для написанного вами стандартного кода C ++ вы всегда должны использовать try / catch ине __try / __except

Кроме того, finally не является C ++ стандартной указанной конструкцией, она работает для вас, потому что это расширение компилятора Microsoft .

3 голосов
/ 13 августа 2011

__try/__except специфичен для Microsoft Если вы хотите, чтобы ваш код компилировался с другими компиляторами (например, g ++) (или) в другой ОС, избегайте их использования и придерживайтесь стандарта try/catch заявления

...