Страницы

Поиск по вопросам

вторник, 28 января 2020 г.

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

#cpp #исключения #деструктор


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

#include 

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;
}

    


Ответы

Ответ 1



Выбрасывать исключение из деструктора не рекомендуется и на это есть ряд причин. это может привести к утечкам памяти, невозможно обеспечить какие-либо гарантии безопасности, исключение из деструктора может вылететь во время раскрутки стека, что приведет к разным последствия в зависимости от объекта. Посмотрим на Ваш код. delete p; Как известно, сначала delete-expression вызывает деструктор и затем освобождает память. Если деструктор выбрасывает исключение, то до освобождения памяти дело уже не дойдет и это приведет к утечке. При реализации, например, контейнеров, невозможно обеспечить какие-либо гарантии относительно безопасности исключений. Например: vector.push_back();//Вектор был не пуст Допустим, произошло выделение памяти. Значит старые элементы копируются в новую область памяти. Если копирование прошло успешно, а при уничтожении старых объектов вылетело исключение, то мы получим часть неуничтоженных объектов и утечку памяти. Если же при копировании вылетело исключение и вектор попытается уничтожить уже сконструированные объекты, а при их уничтожении вылетит исключение, то получим ту же утечку, плюс дальше "полетит" уже другое исключение. Если исключение вылетает из деструктора автоматического объекта во время раскрутки стека, то, в соответствии со стандартом, это приводит к вызову std::terminate 15.2 Constructors and destructors 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) { /*...*/ } Но при этом, если вылетит исключение, мы получим все проблемы указанные выше. Итого: исключение, покинувшее деструктор создает много проблем, и не дает никакой пользы, поэтому общая рекомендация - никогда не позволять исключениям покидать деструктор.

Ответ 2



Вообще-то в деструкторах генерировать исключения категорически не рекомендуется. Как я понимаю (на конкретное место в стандарте не укажу) - фактически деструкторы объявлены как noexcept - а значит, при генерации в них исключения будет вызван terminate. Вот, выполните этот код: #include #include 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 - как результат генерации исключения там, где его быть не должно. "По-моему, так" (с) Пух

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

Отправить комментарий