2015-02-05 3 views
12

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

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

catch(A& a){ 
... 
} 

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

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

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

+1

Меня немного интересует, почему вы задаете этот вопрос; это очень действительный, не очень простой вопрос, но это указывает на то, что вы заботитесь о момент времени, когда вызывается деструктор исключения, который вы ничего не делаете. –

+0

Да, я не уверен в этой последней части. –

+0

@ MarcusMüller: Что случилось с желанием получить знания об инструментах, которые мы используем? –

ответ

5

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

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

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

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

+0

(возможно, это было где-то в другом месте) - можете ли вы объяснить, пожалуйста? –

+3

И только для полноты, удаляя указатель на производный объект с помощью указателя на базовый тип, когда базовый тип не имеет виртуального деструктора, создает ** неопределенное поведение **. Он может запустить базовый деструктор, но он может сделать что-то совершенно другое. –

+0

@Day_Dreamer Извините, это было немного неудобное предложение, но дело в том, что в точке кода, где указатель 'delete'd, нет способа сопоставить его с тем местом, где тот же самый объект был' new'ed. Вот почему вам нужен виртуальный деструктор, иначе нет способа, чтобы код «знал», какой деструктор должен вызывать. – Brian

5

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

Да:

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


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

No:

#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() 
+0

Собственно, ваш образец указывает на другое. Я получаю следующий вывод (используя тот же онлайн-компилятор): ~ B() ~ A() – zdan

+1

Ahem, Lightness, ваша ссылка coliru показывает как '~ B()', так и '~ A()' напечатано ... что что ответ Cornstalks подтверждает ... Just sayin ' –

+0

@InnocentBystander: Я думаю, невозможно определить, является ли этот '~ B()' временным в _throw-expression_, или же это устранено (что является законным), и мы «видеть вывод из' catch'. –

3

В то время как я не цитирую стандарт, кажется, что бросает B и ловить A& приведет обоих A-х и B 's деструкторов вызывался. Live demo:

#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
завершено внутренний scope
завершено основной scope

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

3

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

Всегда.

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