2011-12-18 1 views
2

У меня есть следующий код:Почему деструкторы не называются, когда исключение не попадает в main?

#include <iostream> 
#include <vector> 
#include <tr1/memory> 

struct FooError {}; 

struct Foo 
{ 
    ~Foo() { std::cerr << "~Foo() executed" << std::endl; } 
    explicit Foo(unsigned int index) { if (5 == index) throw FooError(index); }; 
}; 


int main() { 
    typedef std::tr1::shared_ptr<Foo> FooPtr; 
    std::vector<FooPtr> foos; 
    for (unsigned int index = 0; index < 20; ++index) 
    { 
     try 
     { 
      foos.push_back(FooPtr(new Foo(index))); 
     } 
     catch (const FooError&) 
     { 
      std::cerr << "FooError caught" << std::endl; 
     } 
    } 
} 

Я вижу набор ~Foo() выполняется, когда есть try{} catch{} блоки. Когда нет обработчиков исключений, ничего не печатается. Означает ли это, что деструкторы объектов, связанных с стеком, вызывается при обработке исключения? Или ничего не печатается из-за проблем с буферизацией std :: cerr?

ответ

1

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

Вектор фактически хранит все свои данные в куче кстати; поэтому она изменчиваема. Вы можете думать о данных в стеке как указатель на память в куче (которая скрыта от вас).

+1

+1. Я не понимал, что OP говорит о ситуации, когда нет попытки/улова, подумал, что это происходит, когда исключение не выбрасывается. –

+1

Но то, что осталось от моего удаленного ответа, по-прежнему важно - 'Foo' не выделяются в стеке. –

+0

Интересно, почему не происходит раскручивание стека. Что, если есть несколько уровней блоков и ошибка попадает на нижнюю, у меня будут утечки памяти, потому что FooPtr не освобожден? – bananov

3

Сфера действия программы разматывания, будь то через обычное выполнение или с помощью try/throw/catch, происходит только в том случае, если приложение завершает работу, возвращаясь с main. Если приложение выходит через исключение (или через abort() или terminate()), разматывание не происходит, и деструкторы не вызываются.

Это относится как к автоматическим, так и к статическим объектам.

+0

Если мы поймаем исключение, скажем, на полпути между точкой, в которой она возникла, и точкой возврата main(), но затем * rethrow * it, не будут ли деструкторы вызываться только до той точки, где мы поймали исключение? – jrok

+1

«не происходит размотки» - в целом это определяется реализацией, происходит ли это или нет, в этом случае нет. –

+0

@SteveJessop: Не могли бы вы проверить этот пример: http://ideone.com/X7Hv8? Означает ли это, что это реализация определена, будет ли вызываться конструктор foo или нет? Или «foo» всегда будет разрушен? – jrok

2

Деструкторы вызываются (из вектора) после цикла, непосредственно перед выходом программы.

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

+1

«без вызова деструкторов» - вот что произошло в этой реализации, но не гарантировано, что они не будут вызваны (15.3/9 в C++ 11). –

5

Подробнее о том, что происходит из стандарта C++ 03.

  • От 15,3/9 Обработка исключений

    Если ни один обработчик соответствия не найден в программе, функция прекращается() вызывается;

  • От 18.6.3 Аномальная окончания:

    terminate_handler по умолчанию вызовы по осуществлению, прервать().

  • И от 3.6.3/4 Termination:

    Вызова функции void abort(); объявленной в <cstdlib> завершает программу без выполнения деструкторов для объектов автоматической или статической продолжительности хранения и без вызова функций, передаваемых atexit().

Так вот почему ваш foos объект не разрушается (он имеет статическую продолжительность хранения).Однако, даже если вы измените его так, чтобы это была локальная переменная (с автоматической продолжительностью), это может не решить проблему (выделено мной):

Так что для объектов static duration объекты, деструкторы не вызываются, если вы не измените окончание (возможно, позвонить ему exit() вместо abort()). Тем не менее, для автоматических объектов, сохраняется возможную проблему (подчеркивание добавлено):

15.5.1/1 terminate() функция

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

+0

Я не думаю, что «foos» имеет статическую продолжительность хранения, я думаю, что определение находится в функции 'main'. –

+0

@Steve - Я думаю, что вы правы, неправильно с моей стороны. Я обновил ответ. –