Деструктор класса C++, который выдает исключение, назван?

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

#include <iostream>

using namespace std;

class Boda {
    private:
        char *ptr;

    public:
        Boda() {
            ptr = new char [20];
        }
        ~Boda() {
            cout << "calling ~Boda\n";

            delete [] ptr;
        }

        void ouch() {
            throw 99;
        }
};

void bad() {
    Boda b;
    b.ouch();
}

int main() {
    bad();
}

Это кажется тем деструктором ~Boda никогда не называется, таким образом ptr ресурс никогда не освобождается.

Вот вывод программы:

terminate called after throwing an instance of 'int'
Aborted

Таким образом, кажется, что ответ на мой вопрос No.

Но я думал, что стек был раскручен, когда исключение было выдано? Почему не сделал Boda b объект разрушен в моем примере?

Помогите мне понять эту проблему ресурса. Я хочу записать лучшие программы в будущем.

Кроме того, это так называемое RAII?

Спасибо, Boda Cydo.

6
задан bodacydo 27 June 2010 в 23:51
поделиться

3 ответа

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

Однако, если вы добавите блок try-catch вокруг вызова bad () , вы увидите деструктор для вызываемого объекта Boda :

int main() {
    try {
      bad();
    } catch(...) {  // Catch any exception, forcing stack unwinding always
      return -1;
    }
}

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

Такое угловое поведение обычно не является проблемой по отношению к RAII, поскольку обычно основная причина, по которой вы хотите, чтобы деструкторы запускались, - это освобождение памяти, и вся память возвращается обратно в ОС, когда ваша программа все равно завершается. Однако, если ваши деструкторы делают что-то более сложное, например, возможно, удаляют файл блокировки на диске или что-то в этом роде, где это будет иметь значение, вызывала ли программа деструкторы при сбое или нет, вы можете обернуть свой main в блок try-catch, который улавливает все (в любом случае только для выхода при исключении), чтобы гарантировать, что стек всегда раскручивается перед завершением.

8
ответ дан 9 December 2019 в 22:28
поделиться

Деструктор не будет запущен, если в конструкторе возникло исключение.

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

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

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

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

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

2
ответ дан 9 December 2019 в 22:28
поделиться

Попробуйте очистить поток - вы увидите, что деструктор действительно вызван:

cout << "calling ~Boda" << endl;

Это буферизация ввода-вывода, которая задерживает распечатку до такой степени, что завершение программы прерывается перед фактическим выводом.

Изменить:

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

1
ответ дан 9 December 2019 в 22:28
поделиться
Другие вопросы по тегам:

Похожие вопросы: