Почему деструкторы не вызываются, когда исключение не обнаруживается в 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?
4 ответа
Вот подробности того, что происходит со стандартом C++03.
Из 15.3/9 Обработка исключений
Если в программе не найден соответствующий обработчик, вызывается функция terminate();
С 18.6.3 Аварийное завершение:
По умолчанию реализация terminate_handler вызывает abort().
А из 3.6.3/4 прекращение:
Вызов функции
void abort();
объявлено в<cstdlib>
завершает программу, не выполняя деструкторы для объектов автоматической или статической длительности хранения и не вызывая функции, переданные atexit().
Так вот почему ваш foos
объект не разрушается (у него статическая продолжительность хранения). Однако, даже если вы измените его так, чтобы это была локальная переменная (с автоматической продолжительностью), это может не решить проблему (выделение добавлено):
Таким образом, для static duration
объекты, деструкторы не вызываются до тех пор, пока вы не измените обработчик завершения exit()
вместо abort()
). Однако для автоматических объектов остается возможная проблема (выделение добавлено):
15.5.1 / 1
terminate()
функцияВ ситуации, когда соответствующий обработчик не найден, определяется реализацией, будет ли стек разматываться перед вызовом terminate(). Во всех других ситуациях стек не должен быть размотан до вызова terminate().
Раскручивание области действия программы, будь то через обычное выполнение или через try/throw/catch, происходит только в том случае, если приложение завершается, возвращаясь из main
, Если приложение выходит через исключение (или через abort()
или же terminate()
), не происходит раскручивания и не вызывается деструктор.
Это относится как к автоматическим, так и к статическим объектам.
Деструкторы вызываются (из вектора) после цикла, прямо перед выходом из программы.
Если вы не поймаете исключение, вызывается терминат, который прерывает программу без вызова деструкторов.
Если вы поймаете исключение, для очистки памяти будут вызваны деаллокаторы. Если вы не поймаете исключение, приложение просто закроется.
Кстати, вектор хранит все свои данные в куче; поэтому его можно изменять. Вы можете думать о данных в стеке как о указателе на память в куче (которая скрыта от вас).