Как перехватить исключение

270
13 апреля 2018, 16:52

Почему данный код не ловит исключение?

#include <iostream>
class A
{
private:
public:
    A()
    {
        std::cout << "A::A()" << std::endl;
    }
    ~A()
    {
        throw 1;
        std::cout << "A::~A(int)" << std::endl;
    }
};
int main()
{
    A* p = nullptr;
    try
    {
        p = new A();
        delete p;
    }
    catch(...)
    {
        std::cout << "catch(...)" << std::endl;
    }
    return 0;
}
Answer 1

Выбрасывать исключение из деструктора не рекомендуется и на это есть ряд причин.

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

Посмотрим на Ваш код.

delete p;

Как известно, сначала delete-expression вызывает деструктор и затем освобождает память. Если деструктор выбрасывает исключение, то до освобождения памяти дело уже не дойдет и это приведет к утечке.

При реализации, например, контейнеров, невозможно обеспечить какие-либо гарантии относительно безопасности исключений. Например:

vector.push_back();//Вектор был не пуст

Допустим, произошло выделение памяти. Значит старые элементы копируются в новую область памяти. Если копирование прошло успешно, а при уничтожении старых объектов вылетело исключение, то мы получим часть неуничтоженных объектов и утечку памяти. Если же при копировании вылетело исключение и вектор попытается уничтожить уже сконструированные объекты, а при их уничтожении вылетит исключение, то получим ту же утечку, плюс дальше "полетит" уже другое исключение.

Если исключение вылетает из деструктора автоматического объекта во время раскрутки стека, то, в соответствии со стандартом, это приводит к вызову std::terminate

15.2 Constructors and destructors

  1. The process of calling destructors for automatic objects constructed on the path from a try block to a throw-expression is called “stack unwinding.” [Note: If a destructor called during stack unwinding exits with an exception, terminate is called (15.5.1). So destructors should generally catch exceptions and not let them propagate out of the destructor. —end note]

Это цитата из C++03, но в последующих стандартах поведение не поменялось.

Начиная с C++11 все деструкторы по-умолчанию имеют спецификацию исключений noexcept. При нарушении этой спецификации вызывается std::terminate:

Whenever an exception is thrown and the search for a handler (15.3) encounters the outermost block of a function with an exception-specification that does not allow the exception, then,

— if the exception-specification is a dynamic-exception-specification, the function std::unexpected() is called (15.5.2),

— otherwise, the function std::terminate() is called (15.5.1).

То есть, начиная с C++11 любое исключение, покинувшее деструктор, приводит к вызову std::terminate и аварийному завершению программы, тем самым, как минимум, обеспечивая единое поведение и возможность предоставления каких-либо гарантий. Деструктору возможно указать другую спецификацию исключений:

~A() noexcept(false) { /*...*/ }

Но при этом, если вылетит исключение, мы получим все проблемы указанные выше.

Итого: исключение, покинувшее деструктор создает много проблем, и не дает никакой пользы, поэтому общая рекомендация - никогда не позволять исключениям покидать деструктор.

Answer 2

Вообще-то в деструкторах генерировать исключения категорически не рекомендуется. Как я понимаю (на конкретное место в стандарте не укажу) - фактически деструкторы объявлены как noexcept - а значит, при генерации в них исключения будет вызван terminate.

Вот, выполните этот код:

#include <iostream>
#include <stdlib.h>
class A
{
private:
public:
    A()
    {
        std::cout << "A::A()" << std::endl;
    }
    ~A()
    {
        throw 1;
        std::cout << "A::~A(int)" << std::endl;
    }
};

int main()
{
    std::set_terminate([](){ std::cout << "terminate called\n"; exit(1);});
    A* p = nullptr;
    try
    {
        p = new A();
        delete p;
    }
    catch(...)
    {
        std::cout << "catch(...)" << std::endl;
    }
    return 0;
}

Как видите, вызывается terminate - как результат генерации исключения там, где его быть не должно.

"По-моему, так" (с) Пух

READ ALSO
Инициализация односвязного списка

Инициализация односвязного списка

Создание новой связки *begin = new List; в начале проги убрать, это нужно делать в цикле, где условие if(start==NULL)Первым действием в цикле нужно создать...

267
как передавать путь к файлу?

как передавать путь к файлу?

У меня проблемаМне нужно в мою програму передавать путь к файлу, который нужно считать

229
Увеличение переменной внутри цикла

Увеличение переменной внутри цикла

Подскажите, пожалуйста, как мне сделать, чтобы при каждой итерации j значение переменной k увеличивалось на 5? То бишь когда j=2, то k=5, а когда...

228
Через .map() прогнать html

Через .map() прогнать html

Читаю документацию поmap() и не могу сообразить, как прогнать html через него

259