Главная страница
Навигация по странице:

  • 1.

  • 2.

  • 3.

  • Например

  • обработка исключений. Типичная функция, написанная на С, выглядит примерно так


    Скачать 23.72 Kb.
    НазваниеТипичная функция, написанная на С, выглядит примерно так
    Дата30.09.2022
    Размер23.72 Kb.
    Формат файлаdocx
    Имя файлаобработка исключений.docx
    ТипДокументы
    #706900

    Язык С представляет программисту очень ограниченные возможности обработки исключений, возникших при работе программы. В этом отношении С++ намного развитее С. Здесь у программиста существенно большие возможности по непосредственной обработке исключений. Комитет по разработке стандартов С++ предоставил очень простую, но мощную форму обработки исключений.

    Типичная функция, написанная на С, выглядит примерно так:

    long DoSomething()

    {

    long *a, c;

    FILE *b;

    a = malloc(sizeof(long) * 10);

    if (a == NULL)

    return 1;

    b = fopen("something.bah", "rb");

    if (b == NULL) {

    free(a);

    return 2;

    }

    fread(a, sizeof(long), 10, b);

    if (a[0] != 0x10) {

    free(a);

    fclose(b);

    return 3;

    }

    fclose(b);

    c = a[1];

    free(a);

    return c;

    }

    Выглядит не очень, не так ли? Вы целиком и полностью зависите от значений, которые возвращают вам функции и для каждой ошибки вам постоянно нужен код, который ее обрабатывает. Если вы, скажем, в функции работаете хотя бы с 10 указателями (рапределяете память, освобождаете ее и т.д.), то наверняка половину кода функции будет занимать код обработки ошибок. Такая же ситуация будет в коде, вызывающем эту функцию, так как здесь также нужно обработать все возвращаемые коды ошибок.

    Try-catch-throw

    Давайте же разберем основы обработки исключений в С++. Чтобы комфортно работать с исключениями в С++ вам нужно знать лишь три ключевых слова:

    • try (пытаться) - начало блока исключений;

    • catch (поймать) - начало блока, "ловящего" исключение;

    • throw (бросить) - ключевое слово, "создающее" ("возбуждающее") исключение.

    блок try-catch



    Простейший формат защищенного блока имеет вид:

    try {операторы защищенного блока}
    catch(...) {обработчик ошибочной ситуации}

    А теперь пример, демонстрирующий, как применить то, что вы узнали:

    void func()

    {

    try

    {

    throw 1;

    }

    catch(int a)

    {

    cout << "Caught exception number: " << a << endl;

    return;

    }

    cout << "No exception detected!" << endl;

    return;

    }

    Если выполнить этот фрагмент кода, то мы получим следующий результат:

    Caught exception number: 1

    Теперь закоментируйте строку throw 1; и функция выдаст такой результат:

    No exception detected!

    Для их возбуждения используется оператор throw.


    Тип выражения, указанного в операторе throw, определяет тип исключительной ситуации, а значение может быть передано обработчику прерываний. Этот механизм, заявленный как стандартный, представляется весьма экзотическим без использования механизма классов.

    Соответственно, полный формат защищенного блока имеет вид:
    try {операторы защищенного блока}
    {catch-блоки}…

    Catch-блок имеет один из следующих форматов:
    catch (тип) {обработчик ошибочной ситуации}
    catch (тип идентификатор) {обработчик ошибочной ситуации}
    catch (…) {обработчик ошибочной ситуации}


    Первый формат используется, если нам надо указать тип перехватываемого исключения, но не нужно обрабатывать связанное с этим исключением значение (это достигается при использовании второго формата оператора catch). Наконец, третий формат оператора catch позволяет обработать все исключения.

    Обработка исключений, возбужденных оператором throw, идет по следующей схеме:
    1. Создается статическая переменная со значением, заданным в операторе throw. 
    Она будет существовать до тех пор, пока исключение не будет обработано. 
    Если переменная-исключение является объектом класса, при ее создании работает конструктор копирования.
    2. Завершается выполнение защищенного try-блока: раскручивается 
    стек подпрограмм, 
    вызываются деструкторы для тех объектов, время жизни которых истекает и т.д.
    3. Выполняется поиск первого из catch-блоков, который пригоден для обработки созданного 
    исключения. 

    Поиск ведется по следующим критериям:
    — если тип, указанный в catch-блоке, совпадает с типом созданного исключения, 
    или является ссылкой на этот тип;
    — класс, заданный в catch-блоке, является предком класса, заданного в throw, 
    и наследование выполнялось с ключом доступа public;
    — указатель, заданный в операторе throw, может быть преобразован по стандартным правилам к указателю, заданному в catch-блоке.
    — в операторе throw задано многоточие.

    Если нужный обработчик найден, то ему передается управление и, при необходимости, 
    значение оператора throw. Оставшиеся catch-блоки, 
    относящиеся к защищенному блоку, в котором было создано исключение, игнорируются.
    Из указанных правил поиска следует, что очень важен порядок расположения catch-блоков. 
    Так, блок catch(…) должен стоять последним в списке, а блок catch (void *) – после всех блоков с указательными типами.
    Если ни один из catch-блоков, указанных после защищенного блока, не сработал, то исключение считается необработанным. Его обработка может быть продолжена во внешних блоках try (если они, конечно, есть).
    В конце оператора catch может стоять оператор throw без параметров. В этом случае работа catch-блока считается незавершенной а исключение – не обработанным до конца, и происходит поиск соответствующего обработчика на более высоких уровнях.
    Если оператор throw был вызван вне защищенного блока (что чаще всего случается, когда исключение возбуждается в вызванной функции), или если не был найден ни один подходящий обработчик этого исключения, то вызывается стандартная функция terminate(). Она, в свою очередь, вызывает функциюabort() для завершения работы с приложением. Единственное, что доступно программисту в этом случае – зарегистрировать с помощью функции set_terminate свою функцию, которая будет выполняться перед аварийным завершением работы.

    Например:
    void MyTerminate() {
    std::cout << "An error occured!" << std::endl;
    exit(-1);
    }
    int main ()
    {
    set_terminate(MyTerminate);
    throw 0;

    Как видите все очень просто, но если это применить с умом, такой подход покажется вам очень мощным средством обработки ошибок. Catch может "ловить" любой тип данных, так же как и throw может "кинуть" данные любого типа. Т.е. throw AnyClass(); будет правильно работать, так же как и catch (AnyClass &d) {};.

    Как уже было сказано, catch может "ловить" данные любого типа, но вовсе не обязательно при это указывать переменную. Т.е. прекрасно будет работать что-нибудь типа этого:

    catch(dumbclass) { }

    так же, как и

    catch(dumbclass&) { }

    Так же можно "поймать" и все исключения:

    catch(...) { }

    Троеточие в этом случае показывает, что будут пойманы все исключения. При таком подходе нельзя указать имя переменной. В случае, если "кидаются" данные нестандартного типа (экземпляры определенных вами классов, структур и т.д.), лучше "ловить" их по ссылке, иначе вся "кидаемая" переменная будет скопирована в стек вместо того, чтобы просто передать указатель на нее. Если кидаются данные нескольких типов и вы хотите поймать конкретную переменную (вернее, переменную конкретного типа), то можно использовать несколько блоков catch, ловящих "свой" тип данных:

    try {

    throw 1;

    // throw 'a';

    }

    catch (long b) {

    cout << "пойман тип long: " << b << endl;

    }

    catch (char b) {

    cout << "пойман тип char: " << b << endl;

    }"

    Создание" исключений

    Когда возбуждается исключительная ситуация, программа просматривает стек функций до тех пор, пока не находит соответствующий catch. Если оператор catch не найден, STL будет обрабатывать исключение в стандартном обработчике, который делает все менее изящно, чем могли бы сделать вы, показывая какие-то непонятные (для конечного пользователя) сообщения и обычно аварийно завершая программу.

    Однако более важным моментом является то, что пока просматривается стек функций, вызываются деструкторы всех локальных классов, так что вам не нужно забодиться об освобождении памяти и т.п.


    написать администратору сайта