Исключение с не виртуальным деструктором С++

Когда мы выходим из области блока catch, вызывает ли вызов деструктора исключения? (Если мы не отменим его)

Предположим, что у меня есть класс A, и его деструктор не является виртуальным. B наследует A. Предположим, что некоторая функция бросила объект класса B в качестве исключения, и он был пойман блоком catch

catch(A& a){
...
}

Если деструктор исключения следует вызывать при выходе из области catch, в этом случае будет вызываться только деструктор базового класса A?

: кукурузные стебли результат в реальном времени при вызове деструктора класса.

Это противоречит моей логике. Объяснить кого-то?

Ответ 1

Хорошо, кто-то уже ответил на ваш первый вопрос. Я сосредоточусь на этом:

если деструктор исключения должен вызываться при выходе из области catch, в этом случае будет вызван только базовый класс A d'tor?

Реализация всегда будет уничтожать объект исключения должным образом независимо от того, как он пойман. Реализация создает объект исключения, поэтому он знает, как его уничтожить. Это не то же самое, что при вызове delete через указатель, поскольку в этом случае имеется неполная информация о полном типе объекта в этой точке (возможно, это было new ed где-то еще), если только виртуальный деструктор существует.

Если бы это было не так, catch (...) никогда не работал вообще.

Ответ 2

когда мы выходим из области блока catch, вызывается ли деструктор исключения? (Если мы не отменим его)

Да:

[C++11: 15.1/4]: [..] Объект исключений уничтожается после того, как последний оставшийся активный обработчик для исключения выходит любым способом, отличным от ретронирования, или последним объектом типа std::exception_ptr (18.8.5), который ссылается на объект исключения уничтожается, в зависимости от того, что наступит позже. [..]


если деструктор исключения должен вызываться при выходе из области catch, в этом случае будет вызван только базовый класс A d'tor?

Нет:

#include <iostream>

struct A
{
    A() { std::cout << "A()"; }
    A(const A&) { std::cout << "A(const A&)"; }
    A(A&&) { std::cout << "A(A&&)"; }
    ~A() { std::cout << "~A()"; }
};

struct B : A
{
    B() { std::cout << "B()"; }
    B(const B&) { std::cout << "B(const B&)"; }
    B(B&&) { std::cout << "B(B&&)"; }
    ~B() { std::cout << "~B()"; }
};

int main()
{
    try {
        throw B();
    }
    catch (A&) {
    }
}

// Output: A()B()~B()~A()

Ответ 3

Пока я не цитирую стандарт, кажется, что бросать B и ловить A& приведет к вызову как вызываемых деструкторов A, так и B. Живая демонстрация:

#include <iostream>

struct A
{
    ~A() { std::cout << "A::~A" << std::endl; }
};

struct B : public A
{
    ~B() { std::cout << "B::~B" << std::endl; }
};

void throwit()
{
    throw B{};
}

int main()
{
    std::cout << "beginning main scope" << std::endl;

    {
        std::cout << "beginning inner scope" << std::endl;

        try
        {
            std::cout << "calling throwit()" << std::endl;
            throwit();
        }
        catch (A& a)
        {
            std::cout << "caught exception" << std::endl;
        }

        std::cout << "ending inner scope" << std::endl;
    }

    std::cout << "ending main scope" << std::endl;
}

Вывод:

начало основной области
начало внутреннего объема
вызов throwit()
пойманное исключение
B:: ~ B
A:: ~ A
конечный внутренний масштаб
окончание основной области

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

Ответ 4

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

Всегда.

Когда вы полиморфно удаляете объект без виртуального деструктора или завершаете (через оператор delete или явный вызов деструктора) объект неполного типа и надлежащий деструктор нетривиальны, стандарт не говорит, что объект уничтожены. Он не говорит, что вызывается деструктор базового класса. В нем говорится, что у вас есть поведение undefined.