Почему деструкторы не вызываются, когда исключение не обнаруживается в main? - PullRequest
2 голосов
/ 18 декабря 2011

У меня есть следующий код:

#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 ]

5 голосов
/ 18 декабря 2011

Вот подробности того, что происходит со стандартом 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 ().

3 голосов
/ 18 декабря 2011

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

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

2 голосов
/ 18 декабря 2011

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

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

1 голос
/ 18 декабря 2011

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

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

...