Деструктор не вызывается при возникновении исключения в конструкторе - PullRequest
8 голосов
/ 02 апреля 2012

Почему деструктор не вызывается в этом коде?

#include <boost/scoped_ptr.hpp>
#include <iostream>

class MyClass {
boost::scoped_ptr<int> ptr;
public:
MyClass() : ptr(new int) { *ptr = 0; throw; std::cout<<"MyClass Allocated\n"; }
~MyClass() { std::cout<<"MyClass De-allocated\n"; }
int increment() { return ++*ptr; }
};

int main()
{
    boost::scoped_ptr<MyClass> myinst(new MyClass);
    std::cout << myinst->increment() << '\n';
    std::cout << myinst->increment() << '\n';
}

РЕДАКТИРОВАТЬ

Из ответов In понимают, что когда в конструкторе происходит исключение,деструктор не будет вызван.Но если исключение происходит в main (), т.е. после того, как объект MyClass будет полностью создан, будет ли вызван деструктор MyClass?Если нет, то почему это умный указатель?

Добавление кода

#include <boost/scoped_ptr.hpp>
#include <iostream>

class MyClass {
    boost::scoped_ptr<int> ptr;
    public:
    MyClass() : ptr(new int) { *ptr = 0; std::cout<<"MyClass Allocated\n"; }
    ~MyClass() { std::cout<<"MyClass De-allocated\n"; }
    int increment() { return ++*ptr; }
};

int main()
{
    boost::scoped_ptr<MyClass> myinst(new MyClass);
    throw 3;
    std::cout << myinst->increment() << '\n';
    std::cout << myinst->increment() << '\n';
}

Вывод:

MyClass Allocated
terminate called after throwing an instance of 'int'
Aborted

Ответы [ 5 ]

20 голосов
/ 02 апреля 2012

Время жизни объекта C ++ начинается только после успешного завершения его конструктора.
Поскольку исключение было выдано до завершения вызова конструктора, у вас нет завершенного объекта и, следовательно, нет деструктора.

Херб Саттер объясняет это прекрасно , чтобы процитировать его:

Q: Что означает создание исключения из конструктора?

A: Это означает, что строительство не удалось, объект никогда не существовал, его время жизни никогда не начиналось. Действительно, единственный способ сообщить о неудаче построения, то есть о невозможности правильно построить функционирующий объект данного типа, - это вызвать исключение. (Да, существует ныне устаревшее соглашение о программировании, которое гласило: «Если у вас возникли проблемы, просто установите флаг состояния на« плохой »и позвольте вызывающему проверить его с помощью функции IsOK ()». Я прокомментирую это сейчас .)

В биологическом отношении,
зачатие состоялось - конструктор начал - но, несмотря на все усилия, за ним последовал выкидыш - конструктор так и не дотянулся до срока (начала).

Кстати, , поэтому деструктор никогда не будет вызван, если конструктор не преуспел - нечего уничтожать. "It cannot die, for it never lived." Обратите внимание, что это делает фразу "an object whose constructor threw an exception" действительно оксюмороном. Такая вещь даже меньше, чем бывший объект ... она никогда не жила, никогда не была, никогда не дышала первой. Это не объект.

Мы можем обобщить модель конструктора C ++ следующим образом:

Или:

(a) Конструктор обычно возвращается, достигнув своего конца или оператора возврата, и объект существует.

Или:

(b) Конструктор завершается созданием исключения, и объект не только не существует, но и никогда не существовал как объект.

РЕДАКТИРОВАТЬ 1:
Но если исключение произойдет в main(), т.е. после того, как объект MyClass будет полностью создан, будет ли вызван деструктор MyClass?

Да, будет!
Это цель использования scoped_ptr. Как только исключение выдается в main, разматывание стека приведет к освобождению всех локальных объектов, это означает, что myinst (который находится в стеке) также будет освобожден, что в Ход вызовет деструктор MyClass.

См. Ускоренная документация , если сомневаетесь:

Шаблон класса scoped_ptr хранит указатель на динамически размещенный объект. (Динамически размещенные объекты выделяются с помощью нового выражения C ++.) Указанный объект гарантированно будет удален либо при уничтожении scoped_ptr, либо с помощью явного reset

РЕДАКТИРОВАТЬ 2:
Почему ваша отредактированная программа вылетает?
Ваша программа показывает сбои, потому что, вы бросаете исключение, но никогда его не ловите. когда происходит такой сценарий, вызывается специальная функция с именем terminate(), поведение по умолчанию для вызова abort(). Это поведение, определяемое реализацией, независимо от того, является ли стек размотанным до вызова terminate() в этом конкретном сценарии Ref 1 . Кажется, ваша реализация не так, и вы не должны полагаться на это поведение.

Вы можете изменить свою программу следующим образом, чтобы обработать исключение, и вы должны получить ожидаемое поведение:

#include <boost/scoped_ptr.hpp> 
#include <iostream> 

class MyClass { 
    boost::scoped_ptr<int> ptr; 
    public: 
    MyClass() : ptr(new int) { *ptr = 0; std::cout<<"MyClass Allocated\n"; } 
    ~MyClass() { std::cout<<"MyClass De-allocated\n"; } 
    int increment() { return ++*ptr; } 
}; 

void doSomething()
{
    boost::scoped_ptr<MyClass> myinst(new MyClass); 
    throw 3; 
} 

int main() 
{
    try 
    {
        doSomething();    
    }
    catch(int &obj)
    {
        std::cout<<"Exception Handled";
    }

} 

Ref 1 C ++ 03 15.5.1 Функция terminate ()

В следующих ситуациях обработка исключений должна быть прекращена для менее тонких методов обработки ошибок:
....
- когда механизм обработки исключений не может найти обработчик для брошенного исключения (15.3),
....

В таких случаях

  1. void terminate ();

называется (18.6.3).В ситуации, когда соответствующий обработчик не найден, определяется реализацией, будет ли стек разматываться перед вызовом terminate().Во всех других ситуациях стек не должен быть размотан до вызова terminate().Реализация не может досрочно завершить разматывание стека на основании определения, что процесс разматывания в конечном итоге вызовет terminate().

7 голосов
/ 02 апреля 2012

Поскольку вызывать деструктор в этом случае не имеет смысла.

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

0 голосов
/ 02 апреля 2012

Если конструктор выдает исключение, то деструктор класса вызываться не будет, поскольку объект не полностью создан.

Смотрите эту ссылку, как управлять ресурсами в такой ситуации:

http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.10

0 голосов
/ 02 апреля 2012

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

Кроме того, если вы хотите, чтобы ваши отладочные сообщения отображались - особенно если вы имеете дело с аварийным завершением программы - вам действительно следует очистить потоки: т.е. использовать std::endl вместо '\n' в конце строки. (или вставив std::flush)

Несмотря на то, что простое использование '\n' часто срабатывает, бывает достаточно ситуаций, когда оно терпит неудачу, и это действительно действительно сбивает с толку отладку, если у вас нет привычки делать все правильно.

0 голосов
/ 02 апреля 2012

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

Вот один связанный FAQ с сайта Бьярне.

...