Почему исключение не совпадает при перехвате указателя исключения?

#c #exception

Вопрос:

Я новичок в обработке исключений C . Я попытался бросить указатель на исключение и поймал его, но, похоже, позднее пойманное исключение не совпадает с тем, которое я бросил. Вот мой код:

 try {
    bad_exception e2 = bad_exception();
    cout << e2.what() << endl;
    cout << amp;e2 << endl;

    throw amp;e2;
}
catch (bad_exception* ex) {
    cout << ex << endl;
    cout << ex->what() << endl;
    cout << (*ex).what() << endl;
}
 

вывод:
плохое исключение
00CFFCF8
00CFFCF8
Неизвестное исключение
Неизвестное исключение

Я ожидал, что позже появится то же самое название «плохое исключение». Можете ли вы это объяснить?

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

 try {
    char str[5] = "eed8";
    A a = A();
    a.name1 = str;

    cout << a.show() << endl;
    cout << amp;a << endl;

    throw amp;a;
}
catch (A* exp) {
    cout << exp << endl;
    cout << exp->show() << endl;
    cout << exp->name1 << endl;
}
 

выход:
eed8
007BF9DC
007BF9DC
eed8
eed8

Комментарии:

1. Все локальные переменные стека будут уничтожены по мере того, как исключение разматывает стек. Это включает e2 в себя !

2. @0x5453 тыс. Я создал локальный объект, но почему я все еще могу получить к нему доступ в блоке catch? смотрите мои обновленные данные.

3. Почему вы пытаетесь throw указать указатель на локальную переменную? Это кажется мне непреднамеренной ошибкой, которая может сделать это проблемой X-Y. Исключения должны (обычно) создаваться по значению и перехватываться по ссылке. Бросание указателя, хотя и возможно, неидиоматично и сбивает с толку-и может привести к проблемам с областью действия, таким как этот пример, в котором используется висящий указатель. Если это сделано намеренно, то чего именно вы надеетесь достичь с помощью этого шаблона?

4. Одна из «радостей» неопределенного поведения заключается в том, что наблюдаемые эффекты могут соответствовать тому, что вы считаете «работающим», или вы можете видеть поведение, которого вы не ожидали (как вы есть). На самом деле ваш код вызывает функции-члены по висячему указателю (указателю, который когда-то указывал на объект, но этот объект больше не существует).

5. Сбой на моей машине. Неопределенное поведение. Это безумие.

Ответ №1:

Вы должны создавать исключения по значению (обычно).

Проблема здесь в том, что вы указываете указатель на объект. К сожалению, к тому времени, когда указатель пойман, объект, на который он указывает, был уничтожен, и, таким образом, у вас есть недопустимый указатель.

 try {
    bad_exception e2 = bad_exception();
    cout << e2.what() << endl;
    cout << amp;e2 << endl;

    throw amp;e2;
}  // At this point the object "e2" goes out of scope
   // Thus its destructor is called.
   // So the pointer you threw now points at an invlalid object
   // Thus accessign the object via this pointer is UB.

catch (bad_exception* ex) {
    cout << ex << endl;
    cout << ex->what() << endl;
    cout << (*ex).what() << endl;
}
 

Перепишите это так:

 try {
    bad_exception e2 = bad_exception();
    cout << e2.what() << endl;
    cout << amp;e2 << endl;

    throw e2; // Throw a copy.
              // Throw an object that gets copied to a secure location
              // so that when you catch it is still valid.
}
catch (bad_exception constamp; ex) {  // catch and get a reference to the copy.
    cout << amp;ex << endl;
    cout << ex.what() << endl;
}
 

Если вам абсолютно необходимо бросить указатель, используйте new его, чтобы убедиться, что указатель имеет динамический срок службы (но помните, что вам нужно будет его очистить).


Обновить:

Похоже, в исключении есть функция «автоматического удаления»?

Ничего особенного.

Я попытался бросить указатель на обычный объект (не унаследованный от исключения).

В объекте исключения (или любом из его производных классов) нет ничего особенного. Это просто обычный объект. Как и все объекты (которые являются классом), деструктор объекта запускается, когда срок службы объекта истекает. Если A у объекта нет деструктора, то память, используемая объектом, может не измениться (но она не может использоваться другими объектами, которые не являются вашим объектом).

Но я все еще могу получить доступ к объекту и его свойствам в блоке catch.

Это плохая сторона «Неопределенного поведения». Я могу выглядеть так, как будто это работает. Это не работает, это просто выглядит так, как будто это так. И с такой же вероятностью это не сработает в какой-либо другой ситуации.

Комментарии:

1. ткс. Я обновил свой вопрос

2. @irous Ваше обновление ничего не меняет. Вы получаете доступ к объекту после того, как он был уничтожен. Этого объекта не существует. То, что вы получаете, называется «Неопределенное поведение», что означает, что буквально все может произойти, поскольку ваша программа недействительна.

3. да. объект выглядит так, как будто его уничтожают. Я поместил инструкцию печати в деструктор A, и она была напечатана перед входом в блок catch.

4. Использование оператора new для создания исключения также проблематично, поскольку акт создания исключения также может привести к возникновению. Если это произойдет, вместо исключения, которое вы собираетесь создать (в качестве указателя), ваш код выдает std::bad_alloc — что означает, что точка перехвата получает информацию о том, как вашей программе не удалось создать исключение, а не об ошибке, о которой вы (предположительно) хотели сообщить.