Каково поведение вложенных блоков try-catch относительно порядка раскручивания объектов? - PullRequest
0 голосов
/ 03 мая 2020

РЕДАКТИРОВАТЬ: Оказалось, я был смущен уничтожением f второй аргумент, который является копией tmp. Подробности смотрите в моем ответе ниже.

Итак, я экспериментировал с раскруткой C ++ и пришел к этому странному поведению, когда у нас есть вложенные блоки try-catch, а во внешнем мы построили объект. Кажется, что этот объект разрушается в точке перехвата исключения в одном из внутренних обработчиков try-catch.

Также, если мы используем динамическое выделение памяти c, как я показал в этом примере (мы используем new в конструкторе и delete в деструкторе) программа, похоже, выдает сообщение c о диагностике во время выполнения.

Рассмотрим этот код:

#include <stdio.h>

struct test {
    test(int);
    ~test() throw(int);
    int *b;
};

void f5() {
    if (!printf(""))
        throw 9;
}

test f(int, test) throw(int) { test tmp(6); f5();  return tmp; }

test::test(int a) : b(new int(a)) { printf("constructor test@%d\n", *b); }

test::~test() throw(int)
{
    printf("destructor test@%d\n", *b);
    delete b;
}

int main() {
    test tmp(0);
    try {
        test tmp(1);
        try {
            f(7, tmp);
        }
        catch (int) {
            printf("catch(int)@%d\n", 2);
            try {
                test tmphandler(3); f(7, tmp);
            }
            catch (int) {
                printf("catch(int)@%d\n", 4);
            }
        }

        printf("b = %d\n", *tmp.b);

        f(7, tmp);
    }
    catch (int) { printf("catch(int)@%d\n", 5); }
}

Вот это wandbox вывод этой программы:

*** Error in `./prog.exe': double free or corruption (fasttop): 0x00000000020ee180 ***
....back-trace of addresses
constructor test@0
constructor test@1
constructor test@6
destructor test@6
destructor test@1
catch(int)@2
constructor test@3
constructor test@6
destructor test@6
destructor test@3
destructor test@33722768

Я изо всех сил пытаюсь понять, почему в точке catch @ 2 объект во внешней области действия try разрушен (т.е. объект создан @ 1 ).

Ответы [ 2 ]

0 голосов
/ 03 мая 2020

Сначала мне не хватало конструктора копирования, поэтому сообщение c о диагностике во время выполнения. Во-вторых, деструктор был фактически вызван для аргумента f, который копируется из tmp, и поэтому значение 1 печатается при уничтожении указанного объекта, который корректно уничтожается в момент отлова исключения , Вот непонятный фиксированный вариант:

#include <stdio.h>

struct test {
    test(const test&);
    test(int);
    ~test() throw(int);
    int *b;
};

void f5() {
    if (!printf(""))
        throw 9;
}

test f(int, test) throw(int) { test tmp(6); f5();  return tmp; }

test::test(int a) : b(new int(a)) { printf("constructor test@%d\n", *b); }

test::test(const test& a) : b(new int(7)) {}

test::~test() throw(int)
{
    printf("destructor test@%d\n", *b);
    delete b;
}

int main() {
    test tmp(0);
    try {
        test tmp(1);
        try {
            f(7, tmp);
        }
        catch (int) {
            printf("catch(int)@%d\n", 2);
            try {
                test tmphandler(3); f(7, tmp);
            }
            catch (int) {
                printf("catch(int)@%d\n", 4);
            }
        }

        printf("b = %d\n", *tmp.b);

        f(7, tmp);
    }
    catch (int) { printf("catch(int)@%d\n", 5); }
}

С выводом:

constructor test@0
constructor test@1
constructor test@6
destructor test@6
destructor test@7
catch(int)@2
constructor test@3
constructor test@6
destructor test@6
destructor test@7
destructor test@3
catch(int)@4
b = 1
constructor test@6
destructor test@6
destructor test@7
destructor test@1
catch(int)@5
destructor test@0
0 голосов
/ 03 мая 2020

Не уверен насчет стандарта, но недавно я читал книгу Дейтеля о C ++, вот параграф из нее:

17.4 Разматывание стека

Когда выдается исключение, но нет в определенной области видимости стек вызовов функций «размотан», и делается попытка перехватить исключение в следующем внешнем блоке try ... catch. Разматывание стека вызовов функции означает, что функция, в которой исключение не было перехвачено, завершается, все локальные переменные, которые завершили инициализацию в этой функции, уничтожаются, и управление возвращается к оператору, который первоначально вызвал эту функцию. Если блок try содержит этот оператор, делается попытка перехватить исключение. Если блок try не заключает в себе этот оператор, разматывание стека происходит снова. Если ни один обработчик перехвата не поймает это исключение, программа завершит работу.

Я надеюсь, что это ответ на ваш вопрос.

...