Подтвердить что ты не робот

Что произойдет, если удаление [] p завершится с ошибкой?

Предположим, у меня есть указатель на динамически выделенный массив из 10 элементов:

T* p = new T[10];

Позже я хочу выпустить этот массив:

delete[] p;

Что произойдет, если один из деструкторов T выдает исключение? Остальные элементы все еще разрушаются? Выйдет ли память? Будет ли распространено исключение или будет завершено выполнение программы?

Аналогично, что происходит, когда a std::vector<T> уничтожается и один из деструкторов T бросает?

4b9b3361

Ответ 1

Я не вижу, чтобы он явно вызывал в стандарте:

Просто, чтобы они были вызваны в обратном порядке создания

5.3.5 Удалить [expr.delete]

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

И что освобождение памяти будет выполнено даже в случае исключения:

7 Если значение операнда выражения-удаления не является значением нулевого указателя, выражение-выражение вызовет функцию удаления (3.7.4.2). В противном случае не указывается, будет ли вызвана функция освобождения. [ Примечание. Функция деаллокации вызывается независимо от того, создает ли деструктор для объекта или какой-либо элемент массива исключение. - end note]

Я попробовал следующий код в g++, и он показывает, что после исключения больше не вызываются деструкторы:

#include <iostream>
int id = 0;
class X
{
    public:
         X() {   me = id++; std::cout << "C: Start" << me << "\n";}
        ~X() {   std::cout << "C: Done " << me << "\n";
                 if (me == 5) {throw int(1);}
             }
    private:
        int me;
};

int main()
{
    try
    {
        X       data[10];
    }
    catch(...)
    {
        std::cout << "Finished\n";
    }
}

Execute:

> g++ de.cpp
> ./a.out
C: Start0
C: Start1
C: Start2
C: Start3
C: Start4
C: Start5
C: Start6
C: Start7
C: Start8
C: Start9
C: Done 9
C: Done 8
C: Done 7
C: Done 6
C: Done 5
Finished

Что все ведет к этому (очень старый ответ):
исключение исключений из деструктора

Ответ 2

Никогда не делай этого. Если уже есть активное исключение, будет вызван std::terminate: "Bang, вы мертвы" . Ваш деструктор должен. Не. Бросьте. Сопротивление.


edit: соответствующий раздел из стандартных (14882 2003), 15.2 конструкторов и деструкторов [except.dtor]:

15.2.3 Процесс вызова деструкторов для автоматических объектов, построенных по пути от блока try к   throw-expression называется "разворачивание стека". [Примечание: Если деструктор, вызванный во время разворачивания стека, выходит с исключением, вызывается terminate (15.5.1). Таким образом, деструкторы должны обычно перехватывать исключения и   не позволяйте им распространять деструктора. -end note]


Тестовая площадка для игры (в реальной жизни, бросьте что-то, полученное из std::exception, никогда не бросайте int или что-то еще!):

    #include <iostream>
    int main() {
        struct Foo {
            ~Foo() {
                throw 0; // ... fore, std::terminate is called.
            }
        };

        try {
            Foo f;
            throw 0; // First one, will be the active exception once Foo::~Foo()
                     // is executed, there- ...
        } catch (int) {
            std::cout << "caught something" << std::endl;
        }
    }

Ответ 3

5.3.5.7 Если значение операнда delete-expression не является нулевым значение указателя, выражение-выражение вызовет функцию освобождения (3.7.3.2). В противном случае это не определено будет ли функция освобождения называться. [Примечание: освобождение функция вызывается независимо от является ли деструктор для объекта или какой-либо элемент массива бросает исключение. - конечная нота]

Не удалось найти ничего о деструкторах, кроме

В случае массива элементы будут уничтожаются в порядке убывания адреса (т.е. в обратном порядке завершения их конструктора, см. 12.6.2).

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

Ответ 4

Чтобы ответить на второй вопрос, если вы использовали std::vector вместо этого, не было бы необходимости в вызове для удаления, вы не используете указатели (класс вектора внутри я верю, но это не вверх вам управлять).

Ответ 5

Хорошо, вот какой экспериментальный код:

#include <cstddef>
#include <cstdlib>
#include <new>
#include <iostream>

void* operator new[](size_t size) throw (std::bad_alloc)
{
    std::cout << "allocating " << size << " bytes" << std::endl;
    return malloc(size);
}

void operator delete[](void* payload) throw ()
{
    std::cout << "releasing memory at " << payload << std::endl;
    free(payload);
}

struct X
{
    bool throw_during_destruction;

    ~X()
    {
        std::cout << "destructing " << (void*)this << std::endl;
        if (throw_during_destruction) throw 42;
    }
};

int main()
{
    X* p = new X[10]();
    p[5].throw_during_destruction = true;
    p[1].throw_during_destruction = true;
    delete[] p;
}

Запуск кода дал следующий вывод в g++ 4.6.0:

allocating 14 bytes
destructing 0x3e2475
destructing 0x3e2474
destructing 0x3e2473
destructing 0x3e2472
destructing 0x3e2471
terminate called after throwing an instance of 'int'

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application support team for more information.

Итак, кажется, что std::terminate вызывается немедленно, как только первый деструктор бросает. Другие элементы не разрушаются, и память не освобождается. Кто-нибудь может это подтвердить?

Ответ 6

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

Если вы используете вектор, проблема будет такой же, просто не в вашем коде.: -)

Итак, бросание деструкторов - это просто плохая идея (tm).


Как показано на рисунке @Martin, объект, который был сброшен, формально не существует, как только мы входим в деструктор. Другие могут также вернуть свою память.

Однако он, очевидно, содержал некоторые сложные вещи, которые не были правильно очищены от flusher. Если этот объект и другие, следующие за ним в массиве, содержат некоторые блокировки mutex, открывают файлы, кеши базы данных или shared_ptrs, и ни один из них не имеет их деструкторов, мы, вероятно, испытываем большие проблемы.

После вызова std:: terminate в этой точке, чтобы вывести программу из ее нищеты, вам кажется, что вы захотите!